// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
// It is available from http://www.codeplex.com/hyperAddin 
#define FEATURE_MANAGED_ETW

#if !ES_BUILD_STANDALONE
#define FEATURE_ACTIVITYSAMPLING
#endif // !ES_BUILD_STANDALONE

#if ES_BUILD_STANDALONE
#define FEATURE_MANAGED_ETW_CHANNELS
// #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
#endif

using System;
#if FEATURE_ACTIVITYSAMPLING
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Resources;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Microsoft.Win32;

#if ES_BUILD_STANDALONE
using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
#else
using EventDescriptor = System.Diagnostics.Tracing.EventDescriptor;
#endif

using Microsoft.Reflection;

#if !ES_BUILD_AGAINST_DOTNET_V35
using Contract = System.Diagnostics.Contracts.Contract;
#else
using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
#endif

#if ES_BUILD_STANDALONE
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
    /// <summary>
    /// This class is meant to be inherited by a user-defined event source in order to define a managed
    /// ETW provider.
    /// The minimal definition of an EventSource simply specifies a number of ETW event methods that
    /// call one of the EventSource.WriteEvent overloads, <see cref="EventSource.WriteEventCore"/>, 
    /// or <see cref="EventSource.WriteEventWithRelatedActivityIdCore"/> to log them. This functionality 
    /// is sufficient for many users.
    /// <para>
    /// To achieve more control over the ETW provider manifest exposed by the event source type, the 
    /// [<see cref="EventAttribute"/>] attributes can be specified for the ETW event methods.
    /// </para><para>
    /// For very advanced EventSources, it is possible to intercept the commands being given to the
    /// eventSource and change what filtering is done (see EventListener.EnableEvents and 
    /// <see cref="EventListener.DisableEvents"/>) or cause actions to be performed by the eventSource, 
    /// e.g. dumping a data structure (see EventSource.SendCommand and
    /// <see cref="EventSource.OnEventCommand"/>).
    /// </para><para>
    /// The eventSources can be turned on with Windows ETW controllers (e.g. logman), immediately. 
    /// It is also possible to control and intercept the data dispatcher programmatically.  See 
    /// <see cref="EventListener"/> for more.
    /// </para>
    /// </summary>
    /// <remarks>
    /// This is a minimal definition for a custom event source:
    /// <code>
    /// [EventSource(Name="Samples-Demos-Minimal")]
    /// sealed class MinimalEventSource : EventSource
    /// {
    ///     public static MinimalEventSource Log = new MinimalEventSource();
    ///     public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }
    ///     public void Unload(long ImageBase) { WriteEvent(2, ImageBase); }
    ///     private MinimalEventSource() {}
    /// }
    /// </code>
    /// </remarks>
    public partial class EventSource : IDisposable
    {
        /// <summary>
        /// The human-friendly name of the eventSource.  It defaults to the simple name of the class
        /// </summary>
        public string Name { get { return m_name; } }
        /// <summary>
        /// Every eventSource is assigned a GUID to uniquely identify it to the system. 
        /// </summary>
        public Guid Guid { get { return m_guid; } }

        /// <summary>
        /// Returns true if the eventSource has been enabled at all. This is the prefered test
        /// to be performed before a relatively expensive EventSource operation.
        /// </summary>
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        public bool IsEnabled()
        {
            return m_eventSourceEnabled;
        }

        /// <summary>
        /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled. 
        /// 
        /// Note that the result of this function is only an approximation on whether a particular
        /// event is active or not. It is only meant to be used as way of avoiding expensive
        /// computation for logging when logging is not on, therefore it sometimes returns false
        /// positives (but is always accurate when returning false).  EventSources are free to 
        /// have additional filtering.    
        /// </summary>
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        public bool IsEnabled(EventLevel level, EventKeywords keywords)
        {
            return IsEnabled(level, keywords, EventChannel.None);
        }

        /// <summary>
        /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or
        /// if 'keywords' specifies a channel bit for a channel that is enabled.
        /// 
        /// Note that the result of this function only an approximation on whether a particular
        /// event is active or not. It is only meant to be used as way of avoiding expensive
        /// computation for logging when logging is not on, therefore it sometimes returns false
        /// positives (but is always accurate when returning false).  EventSources are free to 
        /// have additional filtering.    
        /// </summary>
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
        {
            if (!m_eventSourceEnabled)
                return false;

            if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
                return false;

#if !FEATURE_ACTIVITYSAMPLING

            return true;

#else // FEATURE_ACTIVITYSAMPLING

            return true;

#if OPTIMIZE_IS_ENABLED
            //================================================================================
            // 2013/03/06 - The code below is a possible optimization for IsEnabled(level, kwd)
            //    in case activity tracing/sampling is enabled. The added complexity of this
            //    code however weighs against having it "on" until we know it's really needed.
            //    For now we'll have this #ifdef-ed out in case we see evidence this is needed.
            //================================================================================            

            // At this point we believe the event is enabled, however we now need to check
            // if we filter because of activity 

            // Optimization, all activity filters also register a delegate here, so if there 
            // is no delegate, we know there are no activity filters, which means that there
            // is no additional filtering, which means that we can return true immediately.  
            if (s_activityDying == null)
                return true;

            // if there's at least one legacy ETW listener we can't filter this
            if (m_legacySessions != null && m_legacySessions.Count > 0)
                return true;

            // if any event ID that triggers a new activity, or "transfers" activities
            // is covered by 'keywords' we can't filter this
            if (unchecked(((long)keywords & m_keywordTriggers)) != 0)
                return true;

            // See if all listeners have activity filters that would block the event.
            for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
            {
                EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
                if (etwSession == null)
                    continue;

                ActivityFilter activityFilter = etwSession.m_activityFilter;
                if (activityFilter == null || 
                    ActivityFilter.GetFilter(activityFilter, this) == null)
                {
                    // No activity filter for ETW, if event is active for ETW, we can't filter.  
                    for (int i = 0; i < m_eventData.Length; i++)
                        if (m_eventData[i].EnabledForETW)
                            return true;
                }
                else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
                    return true;
            }

            // for regular event listeners
            var curDispatcher = m_Dispatchers;
            while (curDispatcher != null)
            {
                ActivityFilter activityFilter = curDispatcher.m_Listener.m_activityFilter;
                if (activityFilter == null)
                {
                    // See if any event is enabled.   
                    for (int i = 0; i < curDispatcher.m_EventEnabled.Length; i++)
                        if (curDispatcher.m_EventEnabled[i])
                            return true;
                }
                else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
                    return true;
                curDispatcher = curDispatcher.m_Next;
            }

            // Every listener has an activity filter that is blocking writing the event, 
            // thus the event is not enabled.  
            return false;
#endif // OPTIMIZE_IS_ENABLED

#endif // FEATURE_ACTIVITYSAMPLING
        }

        /// <summary>
        /// Returns the settings for the event source instance
        /// </summary>
        public EventSourceSettings Settings
        {
            get { return m_config; }
        }

        // Manifest support 
        /// <summary>
        /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.  
        /// This API allows you to compute this without actually creating an instance of the EventSource.   
        /// It only needs to reflect over the type.  
        /// </summary>
        public static Guid GetGuid(Type eventSourceType)
        {
            if (eventSourceType == null)
                throw new ArgumentNullException("eventSourceType");
            Contract.EndContractBlock();

            EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
            string name = eventSourceType.Name;
            if (attrib != null)
            {
                if (attrib.Guid != null)
                {
                    Guid g = Guid.Empty;
#if !ES_BUILD_AGAINST_DOTNET_V35
                    if (Guid.TryParse(attrib.Guid, out g))
                        return g;
#else
                    try { return new Guid(attrib.Guid); }
                    catch (Exception) { }
#endif
                }

                if (attrib.Name != null)
                    name = attrib.Name;
            }

            if (name == null)
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidTypeName"), "eventSourceType");

            return GenerateGuidFromName(name.ToUpperInvariant());       // Make it case insensitive.  
        }
        /// <summary>
        /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.  
        /// This API allows you to compute this without actually creating an instance of the EventSource.   
        /// It only needs to reflect over the type.  
        /// </summary>
        public static string GetName(Type eventSourceType)
        {
            return GetName(eventSourceType, EventManifestOptions.None);
        }

        /// <summary>
        /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
        /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
        /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and
        /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff.
        /// </summary>
        /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
        /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
        /// which it is embedded.  This parameter specifies what name will be used</param>
        /// <returns>The XML data string</returns>
        public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)
        {
            return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
        }
        /// <summary>
        /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
        /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
        /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This
        /// ensures that the entries in the event log will be "optimally" localized.
        /// </summary>
        /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
        /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
        /// which it is embedded.  This parameter specifies what name will be used</param>
        /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified
        /// this returns null when the eventSourceType does not require explicit registration</param>
        /// <returns>The XML data string or null</returns>
        public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags)
        {
            if (eventSourceType == null)
                throw new ArgumentNullException("eventSourceType");
            Contract.EndContractBlock();

            byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
            return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
        }

        // EventListener support
        /// <summary>
        /// returns a list (IEnumerable) of all sources in the appdomain).  EventListeners typically need this.  
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<EventSource> GetSources()
        {
            var ret = new List<EventSource>();
            lock (EventListener.EventListenersLock)
            {
                foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
                {
                    EventSource eventSource = eventSourceRef.Target as EventSource;
                    if (eventSource != null && !eventSource.IsDisposed)
                        ret.Add(eventSource);
                }
            }
            return ret;
        }

        /// <summary>
        /// Send a command to a particular EventSource identified by 'eventSource'.
        /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
        /// callback.  What the EventSource does with the command and its arguments are from 
        /// that point EventSource-specific.  
        /// </summary>
        /// <param name="eventSource">The instance of EventSource to send the command to</param>
        /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
        /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
        public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)
        {
            if (eventSource == null)
                throw new ArgumentNullException("eventSource");

            // User-defined EventCommands should not conflict with the reserved commands.
            if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
                throw new ArgumentException(Environment.GetResourceString("EventSource_InvalidCommand"), "command");

            eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
        }

        // ActivityID support (see also WriteEventWithRelatedActivityIdCore)
        /// <summary>
        /// When a thread starts work that is on behalf of 'something else' (typically another 
        /// thread or network request) it should mark the thread as working on that other work.
        /// This API marks the current thread as working on activity 'activityID'. This API
        /// should be used when the caller knows the thread's current activity (the one being
        /// overwritten) has completed. Otherwise, callers should prefer the overload that
        /// return the oldActivityThatWillContinue (below).
        /// 
        /// All events created with the EventSource on this thread are also tagged with the 
        /// activity ID of the thread. 
        /// 
        /// It is common, and good practice after setting the thread to an activity to log an event
        /// with a 'start' opcode to indicate that precise time/thread where the new activity 
        /// started.
        /// </summary>
        /// <param name="activityId">A Guid that represents the new activity with which to mark 
        /// the current thread</param>
        [System.Security.SecuritySafeCritical]
        public static void SetCurrentThreadActivityId(Guid activityId)
        {
#if FEATURE_ACTIVITYSAMPLING
            Guid newId = activityId;
#endif // FEATURE_ACTIVITYSAMPLING
            // We ignore errors to keep with the convention that EventSources do not throw errors.
            // Note we can't access m_throwOnWrites because this is a static method.  
            if (UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
                UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
                ref activityId) == 0)
            {
#if FEATURE_ACTIVITYSAMPLING
                var activityDying = s_activityDying;
                if (activityDying != null && newId != activityId)
                {
                    if (activityId == Guid.Empty)
                    {
                        activityId = FallbackActivityId;
                    }
                    // OutputDebugString(string.Format("Activity dying: {0} -> {1}", activityId, newId));
                    activityDying(activityId);     // This is actually the OLD activity ID.  
                }
#endif // FEATURE_ACTIVITYSAMPLING
            }
            if (System.Threading.Tasks.TplEtwProvider.Log != null)
                 System.Threading.Tasks.TplEtwProvider.Log.SetActivityId(activityId);
        }

        /// <summary>
        /// When a thread starts work that is on behalf of 'something else' (typically another 
        /// thread or network request) it should mark the thread as working on that other work.
        /// This API marks the current thread as working on activity 'activityID'. It returns 
        /// whatever activity the thread was previously marked with. There is a convention that
        /// callers can assume that callees restore this activity mark before the callee returns. 
        /// To encourage this this API returns the old activity, so that it can be restored later.
        /// 
        /// All events created with the EventSource on this thread are also tagged with the 
        /// activity ID of the thread. 
        /// 
        /// It is common, and good practice after setting the thread to an activity to log an event
        /// with a 'start' opcode to indicate that precise time/thread where the new activity 
        /// started.
        /// </summary>
        /// <param name="activityId">A Guid that represents the new activity with which to mark 
        /// the current thread</param>
        /// <param name="oldActivityThatWillContinue">The Guid that represents the current activity  
        /// which will continue at some point in the future, on the current thread</param>
        [System.Security.SecuritySafeCritical]
        public static void SetCurrentThreadActivityId(Guid activityId, out Guid oldActivityThatWillContinue)
        {
            oldActivityThatWillContinue = activityId;
            // We ignore errors to keep with the convention that EventSources do not throw errors.
            // Note we can't access m_throwOnWrites because this is a static method.  
            UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
                UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
                    ref oldActivityThatWillContinue);

            // We don't call the activityDying callback here because the caller has declared that
            // it is not dying.  
            if (System.Threading.Tasks.TplEtwProvider.Log != null)
                 System.Threading.Tasks.TplEtwProvider.Log.SetActivityId(activityId);
        }

        /// <summary>
        /// Retrieves the ETW activity ID associated with the current thread.
        /// </summary>
        public static Guid CurrentThreadActivityId
        {
            [System.Security.SecurityCritical]
            get
            {
                // We ignore errors to keep with the convention that EventSources do not throw 
                // errors. Note we can't access m_throwOnWrites because this is a static method.
                Guid retVal = new Guid();
                UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
                    UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
                    ref retVal);
                return retVal;
            }
        }

#if !ES_BUILD_STANDALONE
        /// <summary>
        /// This property allows EventSource code to appropriately handle as "different" 
        /// activities started on different threads that have not had an activity created on them.
        /// </summary>
        internal static Guid InternalCurrentThreadActivityId
        {
            [System.Security.SecurityCritical]
            get
            {
                Guid retval = CurrentThreadActivityId;
                if (retval == Guid.Empty)
                {
                    retval = FallbackActivityId;
                }
                return retval;
            }
        }

        internal static Guid FallbackActivityId
        {
            [System.Security.SecurityCritical]
            get
            {
#pragma warning disable 612, 618
                // Managed thread IDs are more aggressively re-used than native thread IDs,
                // so we'll use the latter...
                return new Guid(unchecked((uint)AppDomain.GetCurrentThreadId()),
                                unchecked((ushort)s_currentPid), unchecked((ushort)(s_currentPid >> 16)),
                                0x94, 0x1b, 0x87, 0xd5, 0xa6, 0x5c, 0x36, 0x64);
#pragma warning restore 612, 618
            }
        }
#endif // !ES_BUILD_STANDALONE

        // Error APIs.  (We don't throw by default, but you can probe for status)
        /// <summary>
        /// Because
        /// 
        ///     1) Logging is often optional and thus should not generate fatal errors (exceptions)
        ///     2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
        ///     
        /// The event source constructor does not throw exceptions.  Instead we remember any exception that 
        /// was generated (it is also logged to Trace.WriteLine).
        /// </summary>
        public Exception ConstructionException { get { return m_constructionException; } }

        /// <summary>
        /// EventSources can have arbitrary string key-value pairs associated with them called Traits.  
        /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
        /// (e.g. like the built in ETW listener).   These traits are specififed at EventSource 
        /// construction time and can be retrieved by using this GetTrait API.  
        /// </summary>
        /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
        /// <returns>The value string associated iwth key.  Will return null if there is no such key.</returns>
        public string GetTrait(string key)
        {
            if (m_traits != null)
            {
                for (int i = 0; i < m_traits.Length - 1; i += 2)
                {
                    if (m_traits[i] == key)
                        return m_traits[i + 1];
                }
            }
            return null;
        }

        /// <summary>
        /// Displays the name and GUID for the eventSource for debugging purposes.  
        /// </summary>
        public override string ToString() { return Environment.GetResourceString("EventSource_ToString", Name, Guid); }

        #region protected
        /// <summary>
        /// This is the constructor that most users will use to create their eventSource.   It takes 
        /// no parameters.  The ETW provider name and GUID of the EventSource are determined by the EventSource 
        /// custom attribute (so you can determine these things declaratively).   If the GUID for the eventSource
        /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
        /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
        /// the ETW provider name.
        /// </summary>
        protected EventSource()
            : this(EventSourceSettings.EtwManifestEventFormat)
        {
        }

        /// <summary>
        /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).  
        /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
        /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
        /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
        /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
        /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
        /// 
        /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.  
        /// </summary>
        // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
        protected EventSource(bool throwOnEventWriteErrors)
            : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
        { }

        /// <summary>
        /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)  
        /// </summary>
        protected EventSource(EventSourceSettings settings) : this(settings, null) { }

        /// <summary>
        /// Construct an EventSource with additional non-default settings.  
        /// 
        /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).   
        /// The first string is the key and the second is the value.   These are not interpreted by EventSource
        /// itself but may be interprated the listeners.  Can be fetched with GetTrait(string).   
        /// </summary>
        /// <param name="settings">See EventSourceSettings for more.</param>
        /// <param name="traits">A collection of key-value strings (must be an even number).</param>
        protected EventSource(EventSourceSettings settings, params string[] traits)
        {
            m_config = ValidateSettings(settings);
            Contract.Assert(m_lastCommandException == null);
            var myType = this.GetType();
            Initialize(GetGuid(myType), GetName(myType), traits);
        }

        /// <summary>
        /// This method is called when the eventSource is updated by the controller.  
        /// </summary>
        protected virtual void OnEventCommand(EventCommandEventArgs command) { }

#pragma warning disable 1591
        // optimized for common signatures (no args)
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId)
        {
            WriteEventCore(eventId, 0, null);
        }

        // optimized for common signatures (ints)
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, int arg1)
        {
            if (m_eventSourceEnabled)
            {
                EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
                descrs[0].DataPointer = (IntPtr)(&arg1);
                descrs[0].Size = 4;
                WriteEventCore(eventId, 1, descrs);
            }
        }

        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
        {
            if (m_eventSourceEnabled)
            {
                EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
                descrs[0].DataPointer = (IntPtr)(&arg1);
                descrs[0].Size = 4;
                descrs[1].DataPointer = (IntPtr)(&arg2);
                descrs[1].Size = 4;
                WriteEventCore(eventId, 2, descrs);
            }
        }

        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
        {
            if (m_eventSourceEnabled)
            {
                EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
                descrs[0].DataPointer = (IntPtr)(&arg1);
                descrs[0].Size = 4;
                descrs[1].DataPointer = (IntPtr)(&arg2);
                descrs[1].Size = 4;
                descrs[2].DataPointer = (IntPtr)(&arg3);
                descrs[2].Size = 4;
                WriteEventCore(eventId, 3, descrs);
            }
        }

        // optimized for common signatures (longs)
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, long arg1)
        {
            if (m_eventSourceEnabled)
            {
                EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
                descrs[0].DataPointer = (IntPtr)(&arg1);
                descrs[0].Size = 8;
                WriteEventCore(eventId, 1, descrs);
            }
        }

        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
        {
            if (m_eventSourceEnabled)
            {
                EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
                descrs[0].DataPointer = (IntPtr)(&arg1);
                descrs[0].Size = 8;
                descrs[1].DataPointer = (IntPtr)(&arg2);
                descrs[1].Size = 8;
                WriteEventCore(eventId, 2, descrs);
            }
        }

        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
        {
            if (m_eventSourceEnabled)
            {
                EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
                descrs[0].DataPointer = (IntPtr)(&arg1);
                descrs[0].Size = 8;
                descrs[1].DataPointer = (IntPtr)(&arg2);
                descrs[1].Size = 8;
                descrs[2].DataPointer = (IntPtr)(&arg3);
                descrs[2].Size = 8;
                WriteEventCore(eventId, 3, descrs);
            }
        }

        // optimized for common signatures (strings)
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, string arg1)
        {
            if (m_eventSourceEnabled)
            {
                if (arg1 == null) arg1 = "";
                fixed (char* string1Bytes = arg1)
                {
                    EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
                    descrs[0].DataPointer = (IntPtr)string1Bytes;
                    descrs[0].Size = ((arg1.Length + 1) * 2);
                    WriteEventCore(eventId, 1, descrs);
                }
            }
        }

        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, string arg1, string arg2)
        {
            if (m_eventSourceEnabled)
            {
                if (arg1 == null) arg1 = "";
                if (arg2 == null) arg2 = "";
                fixed (char* string1Bytes = arg1)
                fixed (char* string2Bytes = arg2)
                {
                    EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
                    descrs[0].DataPointer = (IntPtr)string1Bytes;
                    descrs[0].Size = ((arg1.Length + 1) * 2);
                    descrs[1].DataPointer = (IntPtr)string2Bytes;
                    descrs[1].Size = ((arg2.Length + 1) * 2);
                    WriteEventCore(eventId, 2, descrs);
                }
            }
        }

        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3)
        {
            if (m_eventSourceEnabled)
            {
                if (arg1 == null) arg1 = "";
                if (arg2 == null) arg2 = "";
                if (arg3 == null) arg3 = "";
                fixed (char* string1Bytes = arg1)
                fixed (char* string2Bytes = arg2)
                fixed (char* string3Bytes = arg3)
                {
                    EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
                    descrs[0].DataPointer = (IntPtr)string1Bytes;
                    descrs[0].Size = ((arg1.Length + 1) * 2);
                    descrs[1].DataPointer = (IntPtr)string2Bytes;
                    descrs[1].Size = ((arg2.Length + 1) * 2);
                    descrs[2].DataPointer = (IntPtr)string3Bytes;
                    descrs[2].Size = ((arg3.Length + 1) * 2);
                    WriteEventCore(eventId, 3, descrs);
                }
            }
        }

        // optimized for common signatures (string and ints)
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, string arg1, int arg2)
        {
            if (m_eventSourceEnabled)
            {
                if (arg1 == null) arg1 = "";
                fixed (char* string1Bytes = arg1)
                {
                    EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
                    descrs[0].DataPointer = (IntPtr)string1Bytes;
                    descrs[0].Size = ((arg1.Length + 1) * 2);
                    descrs[1].DataPointer = (IntPtr)(&arg2);
                    descrs[1].Size = 4;
                    WriteEventCore(eventId, 2, descrs);
                }
            }
        }

        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3)
        {
            if (m_eventSourceEnabled)
            {
                if (arg1 == null) arg1 = "";
                fixed (char* string1Bytes = arg1)
                {
                    EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
                    descrs[0].DataPointer = (IntPtr)string1Bytes;
                    descrs[0].Size = ((arg1.Length + 1) * 2);
                    descrs[1].DataPointer = (IntPtr)(&arg2);
                    descrs[1].Size = 4;
                    descrs[2].DataPointer = (IntPtr)(&arg3);
                    descrs[2].Size = 4;
                    WriteEventCore(eventId, 3, descrs);
                }
            }
        }

        // optimized for common signatures (string and longs)
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
        {
            if (m_eventSourceEnabled)
            {
                if (arg1 == null) arg1 = "";
                fixed (char* string1Bytes = arg1)
                {
                    EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
                    descrs[0].DataPointer = (IntPtr)string1Bytes;
                    descrs[0].Size = ((arg1.Length + 1) * 2);
                    descrs[1].DataPointer = (IntPtr)(&arg2);
                    descrs[1].Size = 8;
                    WriteEventCore(eventId, 2, descrs);
                }
            }
        }

        // optimized for common signatures (long and string)
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, long arg1, string arg2)
        {
            if (m_eventSourceEnabled)
            {
                if (arg2 == null) arg2 = "";
                fixed (char* string2Bytes = arg2)
                {
                    EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
                    descrs[0].DataPointer = (IntPtr)(&arg1);
                    descrs[0].Size = 8;
                    descrs[1].DataPointer = (IntPtr)string2Bytes;
                    descrs[1].Size = ((arg2.Length + 1) * 2);
                    WriteEventCore(eventId, 2, descrs);
                }
            }
        }

        // optimized for common signatures (int and string)
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, int arg1, string arg2)
        {
            if (m_eventSourceEnabled)
            {
                if (arg2 == null) arg2 = "";
                fixed (char* string2Bytes = arg2)
                {
                    EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
                    descrs[0].DataPointer = (IntPtr)(&arg1);
                    descrs[0].Size = 4;
                    descrs[1].DataPointer = (IntPtr)string2Bytes;
                    descrs[1].Size = ((arg2.Length + 1) * 2);
                    WriteEventCore(eventId, 2, descrs);
                }
            }
        }

        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, byte[] arg1)
        {
            if (m_eventSourceEnabled)
            {
                if (arg1 == null) arg1 = new byte[0];
                int blobSize = arg1.Length;
                fixed (byte* blob = &arg1[0])
                {
                    EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
                    descrs[0].DataPointer = (IntPtr)(&blobSize);
                    descrs[0].Size = 4;
                    descrs[1].DataPointer = (IntPtr)blob;
                    descrs[1].Size = blobSize;
                    WriteEventCore(eventId, 2, descrs);
                }
            }
        }

        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, long arg1, byte[] arg2)
        {
            if (m_eventSourceEnabled)
            {
                if (arg2 == null) arg2 = new byte[0];
                int blobSize = arg2.Length;
                fixed (byte* blob = &arg2[0])
                {
                    EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
                    descrs[0].DataPointer = (IntPtr)(&arg1);
                    descrs[0].Size = 8;
                    descrs[1].DataPointer = (IntPtr)(&blobSize);
                    descrs[1].Size = 4;
                    descrs[2].DataPointer = (IntPtr)blob;
                    descrs[2].Size = blobSize;
                    WriteEventCore(eventId, 3, descrs);
                }
            }
        }

#pragma warning restore 1591

        /// <summary>
        /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
        /// </summary>
        protected internal struct EventData
        {
            /// <summary>
            /// Address where the one argument lives (if this points to managed memory you must ensure the
            /// managed object is pinned.
            /// </summary>
            public IntPtr DataPointer { get { return (IntPtr)m_Ptr; } set { m_Ptr = unchecked((long)value); } }
            /// <summary>
            /// Size of the argument referenced by DataPointer
            /// </summary>
            public int Size { get { return m_Size; } set { m_Size = value; } }

            #region private
            /// <summary>
            /// Initializes the members of this EventData object to point at a previously-pinned
            /// tracelogging-compatible metadata blob.
            /// </summary>
            /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
            /// <param name="size">The size of the metadata blob.</param>
            /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
            [SecurityCritical]
            internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
            {
                this.m_Ptr = (long)(ulong)(UIntPtr)pointer;
                this.m_Size = size;
                this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
            }

            //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
            // the way EventWrite wants it.  
            internal long m_Ptr;
            internal int m_Size;
#pragma warning disable 0649
            internal int m_Reserved;        // Used to pad the size to match the Win32 API
#pragma warning restore 0649
            #endregion
        }

        /// <summary>
        /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
        /// do this, while straightforward, is unsafe.
        /// </summary>
        /// <remarks>
        /// <code>
        ///    protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
        ///    {
        ///        if (IsEnabled())
        ///        {
        ///            if (arg2 == null) arg2 = "";
        ///            fixed (char* string2Bytes = arg2)
        ///            {
        ///                EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
        ///                descrs[0].DataPointer = (IntPtr)(&amp;arg1);
        ///                descrs[0].Size = 8;
        ///                descrs[1].DataPointer = (IntPtr)string2Bytes;
        ///                descrs[1].Size = ((arg2.Length + 1) * 2);
        ///                WriteEventCore(eventId, 2, descrs);
        ///            }
        ///        }
        ///    }
        /// </code>
        /// </remarks>
        [SecurityCritical]
        [CLSCompliant(false)]
        protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
        {
            WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
        }

        /// <summary>
        /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code 
        /// that you use to do this, while straightforward, is unsafe. The only difference from
        /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
        /// </summary>
        /// <remarks>
        /// <code>
        ///    protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
        ///    {
        ///        if (IsEnabled())
        ///        {
        ///            if (arg2 == null) arg2 = "";
        ///            fixed (char* string2Bytes = arg2)
        ///            {
        ///                EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
        ///                descrs[0].DataPointer = (IntPtr)(&amp;arg1);
        ///                descrs[0].Size = 8;
        ///                descrs[1].DataPointer = (IntPtr)string2Bytes;
        ///                descrs[1].Size = ((arg2.Length + 1) * 2);
        ///                WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
        ///            }
        ///        }
        ///    }
        /// </code>
        /// </remarks>
        [SecurityCritical]
        [CLSCompliant(false)]
        protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
        {
            if (m_eventSourceEnabled)
            {
                try
                {
                    Contract.Assert(m_eventData != null);  // You must have initialized this if you enabled the source.
                    if (relatedActivityId != null)
                        ValidateEventOpcodeForTransfer(ref m_eventData[eventId]);

#if FEATURE_MANAGED_ETW
                    if (m_eventData[eventId].EnabledForETW)
                    {
                        EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
                        EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
                        Guid* pActivityId = null;
                        Guid activityId = Guid.Empty;
                        Guid relActivityId = Guid.Empty;

                        if (opcode != EventOpcode.Info && relatedActivityId == null &&
                           ((activityOptions & EventActivityOptions.Disable) == 0))
                        {
                            if (opcode == EventOpcode.Start)
                            {
                                m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
                            }
                            else if (opcode == EventOpcode.Stop)
                            {
                                m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
                            }

                            if (activityId != Guid.Empty)
                                pActivityId = &activityId;
                            if (relActivityId != Guid.Empty)
                                relatedActivityId = &relActivityId;
                        }

#if FEATURE_ACTIVITYSAMPLING
                        // this code should be kept in sync with WriteEventVarargs().
                        SessionMask etwSessions = SessionMask.All;
                        // only compute etwSessions if there are *any* ETW filters enabled...
                        if ((ulong)m_curLiveSessions != 0)
                            etwSessions = GetEtwSessionMask(eventId, relatedActivityId);
                        // OutputDebugString(string.Format("{0}.WriteEvent(id {1}) -> to sessions {2:x}", 
                        //                   m_name, m_eventData[eventId].Name, (ulong) etwSessions));

                        if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
                        {
                            if (!SelfDescribingEvents)
                            {
                                if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
                                {
                                    // OutputDebugString(string.Format("  (1) id {0}, kwd {1:x}", 
                                    //                   m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Keywords));
                                    // by default the Descriptor.Keyword will have the perEventSourceSessionId bit 
                                    // mask set to 0x0f so, when all ETW sessions want the event we don't need to 
                                    // synthesize a new one
                                    if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
                                        ThrowEventSourceException();
                                }
                                else
                                {
                                    long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
                                    // OutputDebugString(string.Format("  (2) id {0}, kwd {1:x}", 
                                    //                   m_eventData[eventId].Name, etwSessions.ToEventKeywords() | (ulong) origKwd));
                                    // only some of the ETW sessions will receive this event. Synthesize a new
                                    // Descriptor whose Keywords field will have the appropriate bits set.
                                    // etwSessions might be 0, if there are legacy ETW listeners that want this event
                                    var desc = new EventDescriptor(
                                        m_eventData[eventId].Descriptor.EventId,
                                        m_eventData[eventId].Descriptor.Version,
                                        m_eventData[eventId].Descriptor.Channel,
                                        m_eventData[eventId].Descriptor.Level,
                                        m_eventData[eventId].Descriptor.Opcode,
                                        m_eventData[eventId].Descriptor.Task,
                                        unchecked((long)etwSessions.ToEventKeywords() | origKwd));

                                    if (!m_provider.WriteEvent(ref desc, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
                                        ThrowEventSourceException();
                                }
                            }
                            else
                            {
                                TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
                                if (tlet == null)
                                {
                                    tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
                                                                        EventTags.None,
                                                                        m_eventData[eventId].Parameters);
                                    Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);

                                }
                                long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
                                // TODO: activity ID support
                                EventSourceOptions opt = new EventSourceOptions
                                {
                                    Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
                                    Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
                                    Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
                                };

                                WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
                            }
                        }
#else
                        if (!SelfDescribingEvents)
                        {
                            if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
                                ThrowEventSourceException();
                        }
                        else
                        {
                            TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
                            if (tlet == null)
                            {
                                tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name, 
                                                                    m_eventData[eventId].Tags, 
                                                                    m_eventData[eventId].Parameters);
                                Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
                            
                            }
                            EventSourceOptions opt = new EventSourceOptions
                            {
                                Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
                                Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
                                Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
                            };

                            WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
                        }
#endif // FEATURE_ACTIVITYSAMPLING
                    }
#endif // FEATURE_MANAGED_ETW

                    if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
                        WriteToAllListeners(eventId, relatedActivityId, eventDataCount, data);
                }
                catch (Exception ex)
                {
                    if (ex is EventSourceException)
                        throw;
                    else
                        ThrowEventSourceException(ex);
                }
            }
        }

        // fallback varags helpers. 
        /// <summary>
        /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
        /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
        /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular 
        /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/> 
        /// check so that the varargs call is not made when the EventSource is not active.  
        /// </summary>
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        protected unsafe void WriteEvent(int eventId, params object[] args)
        {
            WriteEventVarargs(eventId, null, args);
        }

        /// <summary>
        /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
        /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
        /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec).  If your
        /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your 
        /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
        /// check so that the varargs call is not made when the EventSource is not active.
        /// </summary>
        [SecuritySafeCritical]
        protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object[] args)
        {
            WriteEventVarargs(eventId, &relatedActivityId, args);
        }

        #endregion

        #region IDisposable Members
        /// <summary>
        /// Disposes of an EventSource.
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
        /// <summary>
        /// Disposes of an EventSource.
        /// </summary>
        /// <remarks>
        /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
        /// Guidelines:
        /// 1. We may be called more than once: do nothing after the first call.
        /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
        /// </remarks>
        /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
#if FEATURE_MANAGED_ETW
                // Send the manifest one more time to ensure circular buffers have a chance to get to this information
                // even in scenarios with a high volume of ETW events.
                if (m_eventSourceEnabled)
                {
                    try
                    {
                        SendManifest(m_rawManifest);
                    }
                    catch (Exception)
                    { }           // If it fails, simply give up.   
                    m_eventSourceEnabled = false;
                }
                if (m_provider != null)
                {
                    m_provider.Dispose();
                    m_provider = null;
                }
#endif
            }
            m_eventSourceEnabled = false;
        }
        /// <summary>
        /// Finalizer for EventSource
        /// </summary>
        ~EventSource()
        {
            this.Dispose(false);
        }
        #endregion

        #region private
#if FEATURE_ACTIVITYSAMPLING
        internal void WriteStringToListener(EventListener listener, string msg, SessionMask m)
        {
            Contract.Assert(listener == null || (uint)m == (uint)SessionMask.FromId(0));

            if (m_eventSourceEnabled)
            {
                if (listener == null)
                {
                    string eventName = "EventActivityInfo";
                    WriteEventString(0, unchecked((long)m.ToEventKeywords()), eventName, msg);
                }
                else
                {
                    List<object> arg = new List<object>();
                    arg.Add(msg);
                    EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
                    eventCallbackArgs.EventId = 0;
                    eventCallbackArgs.Payload = new ReadOnlyCollection<object>(arg);
                    listener.OnEventWritten(eventCallbackArgs);
                }
            }
        }
#endif
        [SecurityCritical]
        private unsafe void WriteEventRaw(
            ref EventDescriptor eventDescriptor,
            Guid* activityID,
            Guid* relatedActivityID,
            int dataCount,
            IntPtr data)
        {
            if (m_provider == null)
            {
                ThrowEventSourceException();
            }
            else
            {
                if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data))
                    ThrowEventSourceException();
            }
        }


        /// <summary>
        /// Best effort method to notify all listeners (ETW or otherwise) of a "message" of interest.
        /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered 
        /// while writing the message to any one of the listeners will be silently ignored.
        /// </summary>
        internal void WriteString(string msg, SessionMask m, bool isError)
        {
            if (m_provider != null)
            {
                var eventName = isError ? "EventSourceErrorMessage" : "EventSourceMessage";
                WriteEventString(0, unchecked((long)m.ToEventKeywords()), eventName, msg);
                WriteStringToAllListeners(eventName, msg);
            }
        }
        /// <summary>
        /// Best effort method to notify all listeners (ETW or otherwise) of a "message" of interest.
        /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered 
        /// while writing the message to any one of the listeners will be silently ignored.
        /// </summary>
        internal void WriteString(string msg, bool isError = true)
        {
            WriteString(msg, SessionMask.All, isError);
        }

        // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
        // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
        internal EventSource(Guid eventSourceGuid, string eventSourceName)
            : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
        { }

        // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
        internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[] traits = null)
        {
            m_config = ValidateSettings(settings);
            Initialize(eventSourceGuid, eventSourceName, traits);
        }

        /// <summary>
        /// This method is responsible for the common initialization path from our constructors. It must
        /// not leak any exceptions (otherwise, since most EventSource classes define a static member, 
        /// "Log", such an exception would become a cached exception for the initialization of the static
        /// member, and any future access to the "Log" would throw the cached exception).
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
        [SecuritySafeCritical]
        private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[] traits)
        {
            m_traits = traits;
            if (m_traits != null && m_traits.Length % 2 != 0)
                throw new ArgumentException(Environment.GetResourceString("TraitEven"), "traits");

            if (eventSourceGuid == Guid.Empty)
            {
                // this will go to the debugger only, as m_provider is still null 
                ReportOutOfBandMessage(Environment.GetResourceString("EventSource_NeedGuid"), true);
                return;
            }

            if (eventSourceName == null)
            {
                // this will go to the debugger only, as m_provider is still null 
                ReportOutOfBandMessage(Environment.GetResourceString("EventSource_NeedName"), true);
                return;
            }

            m_name = eventSourceName;
            m_guid = eventSourceGuid;
#if FEATURE_ACTIVITYSAMPLING
            m_curLiveSessions = new SessionMask(0);
            m_etwSessionIdMap = new EtwSession[SessionMask.MAX];
#endif // FEATURE_ACTIVITYSAMPLING


            //Enable Implicit Activity tracker
            m_activityTracker = ActivityTracker.Instance;

#if FEATURE_MANAGED_ETW
            m_provider = new OverideEventProvider(this);

            try
            {
                m_provider.Register(eventSourceGuid);
            }
            catch (Exception e)
            {
                // Failed to register.  Don't crash the app, just don't write events to ETW.
                m_provider = null;
                if (m_constructionException == null)
                    m_constructionException = e;
            }
#endif
            // AddEventSource may end up calling in user code (e.g. the event listener may call
            // EnableEvents, or other code, that may throw). We swallow the exception and report 
            // it below in ReportOutOfBandMessage()
            try
            {
                // Add the eventSource to the global (weak) list.  This also sets m_id, which is the
                // index in the list. 
                EventListener.AddEventSource(this);
            }
            catch (Exception e)
            {
                if (m_constructionException == null)
                    m_constructionException = e;
            }

            // lastCommandExceptions are usually more interesting than the other exceptions
            // we may have caught along the way
            if (m_lastCommandException != null)
                m_constructionException = m_lastCommandException;

            if (m_constructionException != null)
            {
                Contract.Assert(m_eventSourceEnabled == false);
                ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": "
                                + m_constructionException.Message, false);
                m_eventSourceEnabled = false;       // This is insurance, it should still be off.    
            }

            if (m_provider != null)
            {
#if !ES_BUILD_STANDALONE
                // API available on OS >= Win 8 and patched Win 7.
                // Disable only for FrameworkEventSource to avoid recursion inside 
                // exception handling.
                var osVer = Environment.OSVersion.Version.Major * 10 + Environment.OSVersion.Version.Minor;
                if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || osVer >= 62)
#endif
                {
                    // Create and register our provider traits:
                    this.InitializeProviderMetadata();
                    int setInformationResult;
                    fixed (void* providerMetadata = this.providerMetadata)
                    {
                        setInformationResult = m_provider.SetInformation(
                            UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
                            providerMetadata,
                            this.providerMetadata.Length);
                    }
                }
            }

            // report any possible errors
            ReportOutOfBandMessage(null, true);

            // We are logically completely initialized at this point.  
            m_completelyInited = true;

#if FEATURE_ACTIVITYSAMPLING
            // we cue sending sampling info here based on whether we had to defer sending
            // the manifest
            // note: we do *not* send sampling info to any EventListeners because
            // the following common code pattern would cause an AV:
            // class MyEventSource: EventSource
            // {
            //    public static EventSource Log; 
            // }
            // class MyEventListener: EventListener
            // {
            //    protected override void OnEventWritten(...)
            //    { MyEventSource.Log.anything; } <-- AV, as the static Log was not set yet
            // }
            if (m_eventSourceEnabled && m_deferedSendManifest)
                ReportActivitySamplingInfo(null, m_curLiveSessions);
#endif // FEATURE_ACTIVITYSAMPLING

            // If we are active and we have not sent our manifest, do so now.  
            if (m_eventSourceEnabled && !SelfDescribingEvents && m_deferedSendManifest)
                SendManifest(m_rawManifest);
        }

        private static string GetName(Type eventSourceType, EventManifestOptions flags)
        {
            if (eventSourceType == null)
                throw new ArgumentNullException("eventSourceType");
            Contract.EndContractBlock();

            EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
            if (attrib != null && attrib.Name != null)
                return attrib.Name;

            return eventSourceType.Name;
        }

        /// <summary>
        /// Implements the SHA1 hashing algorithm. Note that this
        /// implementation is for hashing public information. Do not
        /// use this code to hash private data, as this implementation does
        /// not take any steps to avoid information disclosure.
        /// </summary>
        private struct Sha1ForNonSecretPurposes
        {
            private long length; // Total message length in bits
            private uint[] w; // Workspace
            private int pos; // Length of current chunk in bytes

            /// <summary>
            /// Call Start() to initialize the hash object.
            /// </summary>
            public void Start()
            {
                if (this.w == null)
                {
                    this.w = new uint[85];
                }

                this.length = 0;
                this.pos = 0;
                this.w[80] = 0x67452301;
                this.w[81] = 0xEFCDAB89;
                this.w[82] = 0x98BADCFE;
                this.w[83] = 0x10325476;
                this.w[84] = 0xC3D2E1F0;
            }

            /// <summary>
            /// Adds an input byte to the hash.
            /// </summary>
            /// <param name="input">Data to include in the hash.</param>
            public void Append(byte input)
            {
                this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
                if (64 == ++this.pos)
                {
                    this.Drain();
                }
            }

            /// <summary>
            /// Adds input bytes to the hash.
            /// </summary>
            /// <param name="input">
            /// Data to include in the hash. Must not be null.
            /// </param>
            public void Append(byte[] input)
            {
                foreach (var b in input)
                {
                    this.Append(b);
                }
            }

            /// <summary>
            /// Retrieves the hash value.
            /// Note that after calling this function, the hash object should
            /// be considered uninitialized. Subsequent calls to Append or
            /// Finish will produce useless results. Call Start() to
            /// reinitialize.
            /// </summary>
            /// <param name="output">
            /// Buffer to receive the hash value. Must not be null.
            /// Up to 20 bytes of hash will be written to the output buffer.
            /// If the buffer is smaller than 20 bytes, the remaining hash
            /// bytes will be lost. If the buffer is larger than 20 bytes, the
            /// rest of the buffer is left unmodified.
            /// </param>
            public void Finish(byte[] output)
            {
                long l = this.length + 8 * this.pos;
                this.Append(0x80);
                while (this.pos != 56)
                {
                    this.Append(0x00);
                }

                unchecked
                {
                    this.Append((byte)(l >> 56));
                    this.Append((byte)(l >> 48));
                    this.Append((byte)(l >> 40));
                    this.Append((byte)(l >> 32));
                    this.Append((byte)(l >> 24));
                    this.Append((byte)(l >> 16));
                    this.Append((byte)(l >> 8));
                    this.Append((byte)l);

                    int end = output.Length < 20 ? output.Length : 20;
                    for (int i = 0; i != end; i++)
                    {
                        uint temp = this.w[80 + i / 4];
                        output[i] = (byte)(temp >> 24);
                        this.w[80 + i / 4] = temp << 8;
                    }
                }
            }

            /// <summary>
            /// Called when this.pos reaches 64.
            /// </summary>
            private void Drain()
            {
                for (int i = 16; i != 80; i++)
                {
                    this.w[i] = Rol1((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]));
                }

                unchecked
                {
                    uint a = this.w[80];
                    uint b = this.w[81];
                    uint c = this.w[82];
                    uint d = this.w[83];
                    uint e = this.w[84];

                    for (int i = 0; i != 20; i++)
                    {
                        const uint k = 0x5A827999;
                        uint f = (b & c) | ((~b) & d);
                        uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
                    }

                    for (int i = 20; i != 40; i++)
                    {
                        uint f = b ^ c ^ d;
                        const uint k = 0x6ED9EBA1;
                        uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
                    }

                    for (int i = 40; i != 60; i++)
                    {
                        uint f = (b & c) | (b & d) | (c & d);
                        const uint k = 0x8F1BBCDC;
                        uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
                    }

                    for (int i = 60; i != 80; i++)
                    {
                        uint f = b ^ c ^ d;
                        const uint k = 0xCA62C1D6;
                        uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
                    }

                    this.w[80] += a;
                    this.w[81] += b;
                    this.w[82] += c;
                    this.w[83] += d;
                    this.w[84] += e;
                }

                this.length += 512; // 64 bytes == 512 bits
                this.pos = 0;
            }

            private static uint Rol1(uint input)
            {
                return (input << 1) | (input >> 31);
            }

            private static uint Rol5(uint input)
            {
                return (input << 5) | (input >> 27);
            }

            private static uint Rol30(uint input)
            {
                return (input << 30) | (input >> 2);
            }
        }

        private static Guid GenerateGuidFromName(string name)
        {
            byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
            var hash = new Sha1ForNonSecretPurposes();
            hash.Start();
            hash.Append(namespaceBytes);
            hash.Append(bytes);
            Array.Resize(ref bytes, 16);
            hash.Finish(bytes);

            bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50));    // Set high 4 bits of octet 7 to 5, as per RFC 4122
            return new Guid(bytes);
        }

        [SecurityCritical]
        private unsafe object DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
        {
            IntPtr dataPointer = data->DataPointer;
            // advance to next EventData in array
            ++data;

            Type dataType = m_eventData[eventId].Parameters[parameterId].ParameterType;

        Again:
            if (dataType == typeof(IntPtr))
            {
                return *((IntPtr*)dataPointer);
            }
            else if (dataType == typeof(int))
            {
                return *((int*)dataPointer);
            }
            else if (dataType == typeof(uint))
            {
                return *((uint*)dataPointer);
            }
            else if (dataType == typeof(long))
            {
                return *((long*)dataPointer);
            }
            else if (dataType == typeof(ulong))
            {
                return *((ulong*)dataPointer);
            }
            else if (dataType == typeof(byte))
            {
                return *((byte*)dataPointer);
            }
            else if (dataType == typeof(sbyte))
            {
                return *((sbyte*)dataPointer);
            }
            else if (dataType == typeof(short))
            {
                return *((short*)dataPointer);
            }
            else if (dataType == typeof(ushort))
            {
                return *((ushort*)dataPointer);
            }
            else if (dataType == typeof(float))
            {
                return *((float*)dataPointer);
            }
            else if (dataType == typeof(double))
            {
                return *((double*)dataPointer);
            }
            else if (dataType == typeof(decimal))
            {
                return *((decimal*)dataPointer);
            }
            else if (dataType == typeof(bool))
            {
                // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
                if (*((int*)dataPointer) == 1)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else if (dataType == typeof(Guid))
            {
                return *((Guid*)dataPointer);
            }
            else if (dataType == typeof(char))
            {
                return *((char*)dataPointer);
            }
            else if (dataType == typeof(DateTime))
            {
                long dateTimeTicks = *((long*)dataPointer);
                return DateTime.FromFileTimeUtc(dateTimeTicks);
            }
            else if (dataType == typeof(byte[]))
            {
                // byte[] are written to EventData* as an int followed by a blob
                int cbSize = *((int*)dataPointer);
                byte[] blob = new byte[cbSize];
                dataPointer = data->DataPointer;
                data++;
                for (int i = 0; i < cbSize; ++i)
                    blob[i] = *((byte*)dataPointer);
                return blob;
            }
            else if (dataType == typeof(byte*))
            {
                return null;
            }
            else
            {
                if (dataType.IsEnum())
                {
                    dataType = Enum.GetUnderlyingType(dataType);
                    goto Again;
                }

                // Everything else is marshaled as a string.
                // ETW strings are NULL-terminated, so marshal everything up to the first
                // null in the string.
                return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
            }
        }

        // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
        // eventSource).  
        private EventDispatcher GetDispatcher(EventListener listener)
        {
            EventDispatcher dispatcher = m_Dispatchers;
            while (dispatcher != null)
            {
                if (dispatcher.m_Listener == listener)
                    return dispatcher;
                dispatcher = dispatcher.m_Next;
            }
            return dispatcher;
        }

        [SecurityCritical]
        private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)
        {
            if (m_eventSourceEnabled)
            {
                try
                {
                    Contract.Assert(m_eventData != null);  // You must have initialized this if you enabled the source.  
                    if (childActivityID != null)
                        ValidateEventOpcodeForTransfer(ref m_eventData[eventId]);

#if FEATURE_MANAGED_ETW
                    if (m_eventData[eventId].EnabledForETW)
                    {
                        Guid* pActivityId = null;
                        Guid activityId = Guid.Empty;
                        Guid relatedActivityId = Guid.Empty;
                        EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
                        EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;

                        if (childActivityID == null &&
                           ((activityOptions & EventActivityOptions.Disable) == 0))
                        {
                            if (opcode == EventOpcode.Start)
                            {
                                m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
                            }
                            else if (opcode == EventOpcode.Stop)
                            {
                                m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
                            }

                            if (activityId != Guid.Empty)
                                pActivityId = &activityId;
                            if (relatedActivityId != Guid.Empty)
                                childActivityID = &relatedActivityId;
                        }

#if FEATURE_ACTIVITYSAMPLING
                        // this code should be kept in sync with WriteEventWithRelatedActivityIdCore().
                        SessionMask etwSessions = SessionMask.All;
                        // only compute etwSessions if there are *any* ETW filters enabled...
                        if ((ulong)m_curLiveSessions != 0)
                            etwSessions = GetEtwSessionMask(eventId, childActivityID);

                        if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
                        {
                            if (!SelfDescribingEvents)
                            {
                                if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
                                {
                                    // by default the Descriptor.Keyword will have the perEventSourceSessionId bit 
                                    // mask set to 0x0f so, when all ETW sessions want the event we don't need to 
                                    // synthesize a new one
                                    if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
                                        ThrowEventSourceException();
                                }
                                else
                                {
                                    long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
                                    // only some of the ETW sessions will receive this event. Synthesize a new
                                    // Descriptor whose Keywords field will have the appropriate bits set.
                                    var desc = new EventDescriptor(
                                        m_eventData[eventId].Descriptor.EventId,
                                        m_eventData[eventId].Descriptor.Version,
                                        m_eventData[eventId].Descriptor.Channel,
                                        m_eventData[eventId].Descriptor.Level,
                                        m_eventData[eventId].Descriptor.Opcode,
                                        m_eventData[eventId].Descriptor.Task,
                                        unchecked((long)(ulong)etwSessions | origKwd));

                                    if (!m_provider.WriteEvent(ref desc, pActivityId, childActivityID, args))
                                        ThrowEventSourceException();
                                }
                            }
                            else
                            {
                                TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
                                if (tlet == null)
                                {
                                    tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
                                                                        EventTags.None,
                                                                        m_eventData[eventId].Parameters);
                                    Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);

                                }
                                long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
                                EventSourceOptions opt = new EventSourceOptions
                                {
                                    Keywords = (EventKeywords)unchecked((long)(ulong)etwSessions | origKwd),
                                    Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
                                    Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
                                };

                                WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
                            }
                        }
#else
                        if (!SelfDescribingEvents)
                        {
                            if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
                                ThrowEventSourceException();
                        }
                        else
                        {
                            TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
                            if (tlet == null)
                            {
                                tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
                                                                    EventTags.None,
                                                                    m_eventData[eventId].Parameters);
                                Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);

                            }
                            EventSourceOptions opt = new EventSourceOptions
                            {
                                Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
                                Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
                                Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
                            };

                            WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
                        }
#endif // FEATURE_ACTIVITYSAMPLING
                    }
#endif // FEATURE_MANAGED_ETW
                    if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
                    {
#if !ES_BUILD_STANDALONE
                        // Mentain old behavoir - object identity is preserved
                        if (!System.Runtime.Versioning.BinaryCompatibility.TargetsAtLeast_Desktop_V4_5_3)
                        {
                            WriteToAllListeners(eventId, childActivityID, args);
                        }
                        else
#endif // !ES_BUILD_STANDALONE
                        {
                            object[] serializedArgs = SerializeEventArgs(eventId, args);
                            WriteToAllListeners(eventId, childActivityID, serializedArgs);
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (ex is EventSourceException)
                        throw;
                    else
                        ThrowEventSourceException(ex);
                }
            }
        }

        [SecurityCritical]
        unsafe private object[] SerializeEventArgs(int eventId, object[] args)
        {
            TraceLoggingEventTypes eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
            if (eventTypes == null)
            {
                eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
                                                    EventTags.None,
                                                    m_eventData[eventId].Parameters);
                Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
            }
            var eventData = new object[eventTypes.typeInfos.Length];
            for (int i = 0; i < eventTypes.typeInfos.Length; i++)
            {
                eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
            }
            return eventData;
        }

        [SecurityCritical]
        unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
        {
            int paramCount = m_eventData[eventId].Parameters.Length;
            if (eventDataCount != paramCount)
            {
                ReportOutOfBandMessage(Environment.GetResourceString("EventSource_EventParametersMismatch", eventId, eventDataCount, paramCount), true);
                paramCount = Math.Min(paramCount, eventDataCount);
            }

            object[] args = new object[paramCount];

            EventSource.EventData* dataPtr = data;
            for (int i = 0; i < paramCount; i++)
                args[i] = DecodeObject(eventId, i, ref dataPtr);
            WriteToAllListeners(eventId, childActivityID, args);
        }

        // helper for writing to all EventListeners attached the current eventSource.  
        [SecurityCritical]
        unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, params object[] args)
        {
            EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
            eventCallbackArgs.EventId = eventId;
            if (childActivityID != null)
                eventCallbackArgs.RelatedActivityId = *childActivityID;
            eventCallbackArgs.EventName = m_eventData[eventId].Name;
            eventCallbackArgs.Message = m_eventData[eventId].Message;
            eventCallbackArgs.Payload = new ReadOnlyCollection<object>(args);

            DisptachToAllListeners(eventId, childActivityID, eventCallbackArgs);
        }
                
        [SecurityCritical]
        private unsafe void DisptachToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)
        {
            Exception lastThrownException = null;
            for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
            {
                Contract.Assert(dispatcher.m_EventEnabled != null);
                if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
                {
#if FEATURE_ACTIVITYSAMPLING
                    var activityFilter = dispatcher.m_Listener.m_activityFilter;
                    // order below is important as PassesActivityFilter will "flow" active activities
                    // even when the current EventSource doesn't have filtering enabled. This allows
                    // interesting activities to be updated so that sources that do sample can get
                    // accurate data
                    if (activityFilter == null ||
                        ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
                                                            m_eventData[eventId].TriggersActivityTracking > 0,
                                                            this, eventId) ||
                        !dispatcher.m_activityFilteringEnabled)
#endif // FEATURE_ACTIVITYSAMPLING
                    {
                        try
                        {
                            dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
                        }
                        catch (Exception e)
                        {
                            ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
                                 + e.Message, false);
                            lastThrownException = e;
                        }
                    }
                }
            }

            if (lastThrownException != null)
            {
                throw new EventSourceException(lastThrownException);
            }
        }

        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        private unsafe void WriteEventString(EventLevel level, long keywords, string eventName, string msgString)
        {
            if (m_provider != null)
            {
                EventSourceOptions opt = new EventSourceOptions
                {
                    Keywords = (EventKeywords)unchecked(keywords),
                    Level = level
                };
                var msg = new { message = msgString };
                var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
                WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
            }
        }

        /// <summary>
        /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered 
        /// while writing the message to any one of the listeners will be silently ignored.
        /// </summary>
        private void WriteStringToAllListeners(string eventName, string msg)
        {
            EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
            eventCallbackArgs.EventId = 0;
            eventCallbackArgs.Message = msg;
            eventCallbackArgs.EventName = eventName;

            for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
            {
                // skip listeners that weren't correctly initialized
                if (dispatcher.m_EventEnabled == null)
                {
                    continue;
                }
                // if there's *any* enabled event on the dispatcher we'll write out the string
                // otherwise we'll treat the listener as disabled and skip it
                bool dispatcherEnabled = false;
                for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
                {
                    if (dispatcher.m_EventEnabled[evtId])
                    {
                        dispatcherEnabled = true;
                        break;
                    }
                }
                try
                {
                    if (dispatcherEnabled)
                        dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
                }
                catch
                {
                    // ignore any exceptions thrown by listeners' OnEventWritten
                }
            }
        }

#if FEATURE_ACTIVITYSAMPLING
        [SecurityCritical]
        unsafe private SessionMask GetEtwSessionMask(int eventId, Guid* childActivityID)
        {
            SessionMask etwSessions = new SessionMask();

            for (int i = 0; i < SessionMask.MAX; ++i)
            {
                EtwSession etwSession = m_etwSessionIdMap[i];
                if (etwSession != null)
                {
                    ActivityFilter activityFilter = etwSession.m_activityFilter;
                    // PassesActivityFilter() will flow "interesting" activities, so make sure
                    // to perform this test first, before ORing with ~m_activityFilteringForETWEnabled
                    // (note: the first test for !m_activityFilteringForETWEnabled[i] ensures we
                    //  do not fire events indiscriminately, when no filters are specified, but only 
                    //  if, in addition, the session did not also enable ActivitySampling)
                    if (activityFilter == null && !m_activityFilteringForETWEnabled[i] ||
                        activityFilter != null &&
                            ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
                                m_eventData[eventId].TriggersActivityTracking > 0, this, eventId) ||
                        !m_activityFilteringForETWEnabled[i])
                    {
                        etwSessions[i] = true;
                    }
                }
            }
            // flow "interesting" activities for all legacy sessions in which there's some 
            // level of activity tracing enabled (even other EventSources)
            if (m_legacySessions != null && m_legacySessions.Count > 0 &&
                (EventOpcode)m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send)
            {
                // only calculate InternalCurrentThreadActivityId once
                Guid* pCurrentActivityId = null;
                Guid currentActivityId;
                foreach (var legacyEtwSession in m_legacySessions)
                {
                    if (legacyEtwSession == null)
                        continue;

                    ActivityFilter activityFilter = legacyEtwSession.m_activityFilter;
                    if (activityFilter != null)
                    {
                        if (pCurrentActivityId == null)
                        {
                            currentActivityId = InternalCurrentThreadActivityId;
                            pCurrentActivityId = &currentActivityId;
                        }
                        ActivityFilter.FlowActivityIfNeeded(activityFilter, pCurrentActivityId, childActivityID);
                    }
                }
            }

            return etwSessions;
        }
#endif // FEATURE_ACTIVITYSAMPLING

        /// <summary>
        /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
        /// It is possible that eventSources turn off the event based on additional filtering criteria.  
        /// </summary>
        private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
        {
            if (!enable)
                return false;

            EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
            EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));

#if FEATURE_MANAGED_ETW_CHANNELS
            EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
#else
            EventChannel channel = EventChannel.None;
#endif

            return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
        }

        private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
                                                          EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
        {
            if (!enabled)
                return false;

            // does is pass the level test?
            if ((currentLevel != 0) && (currentLevel < eventLevel))
                return false;

            // if yes, does it pass the keywords test?
            if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
            {
#if FEATURE_MANAGED_ETW_CHANNELS
                // is there a channel with keywords that match currentMatchAnyKeyword?
                if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
                {
                    EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
                    if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
                        return false;
                }
                else
#endif
                {
                    if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
                        return false;
                }
            }
            return true;

        }
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        private void ThrowEventSourceException(Exception innerEx = null)
        {
            // If we fail during ouf of band logging we may end up trying 
            // to throw another EventSourceException, thus hitting a StackOverflowException. 
            // Avoid StackOverflow by making sure we do not recursively call this method.
            if (m_EventSourceExceptionRecurenceCount > 0)
                return;
            try
            {
                m_EventSourceExceptionRecurenceCount++;

                switch (EventProvider.GetLastWriteEventError())
                {
                    case EventProvider.WriteEventErrorCode.EventTooBig:
                        ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_EventTooBig"), true);
                        if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_EventTooBig"), innerEx);
                        break;
                    case EventProvider.WriteEventErrorCode.NoFreeBuffers:
                        ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_NoFreeBuffers"), true);
                        if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_NoFreeBuffers"), innerEx);
                        break;
                    case EventProvider.WriteEventErrorCode.NullInput:
                        ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_NullInput"), true);
                        if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_NullInput"), innerEx);
                        break;
                    case EventProvider.WriteEventErrorCode.TooManyArgs:
                        ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_TooManyArgs"), true);
                        if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_TooManyArgs"), innerEx);
                        break;
                    default:
                        if (innerEx != null)
                            ReportOutOfBandMessage("EventSourceException: " + innerEx.GetType() + ":" + innerEx.Message, true);
                        else
                            ReportOutOfBandMessage("EventSourceException", true);
                        if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
                        break;
                }
            }
            finally
            {
                m_EventSourceExceptionRecurenceCount--;
            }

        }

        private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData)
        {
            if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
                (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive)
            {
                ThrowEventSourceException();
            }
        }

        internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string eventName)
        {
            if (opcode == EventOpcode.Info)
            {
                if (eventName.EndsWith(s_ActivityStartSuffix))
                {
                    return EventOpcode.Start;
                }
                else if (eventName.EndsWith(s_ActivityStopSuffix))
                {
                    return  EventOpcode.Stop;
                }
            }

            return opcode;
        }

#if FEATURE_MANAGED_ETW
        /// <summary>
        /// This class lets us hook the 'OnEventCommand' from the eventSource.  
        /// </summary>
        private class OverideEventProvider : EventProvider
        {
            public OverideEventProvider(EventSource eventSource)
            {
                this.m_eventSource = eventSource;
            }
            protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments,
                                                              int perEventSourceSessionId, int etwSessionId)
            {
                // We use null to represent the ETW EventListener.  
                EventListener listener = null;
                m_eventSource.SendCommand(listener, perEventSourceSessionId, etwSessionId,
                                          (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
            }
            private EventSource m_eventSource;
        }
#endif

        /// <summary>
        /// Used to hold all the static information about an event.  This includes everything in the event
        /// descriptor as well as some stuff we added specifically for EventSource. see the
        /// code:m_eventData for where we use this.  
        /// </summary>
        internal struct EventMetadata
        {
            public EventDescriptor Descriptor;
            public EventTags Tags;
            public bool EnabledForAnyListener;      // true if any dispatcher has this event turned on
            public bool EnabledForETW;              // is this event on for the OS ETW data dispatcher?
#if !FEATURE_ACTIVITYSAMPLING
#pragma warning disable 0649
#endif
            public byte TriggersActivityTracking;   // count of listeners that marked this event as trigger for start of activity logging.
#if !FEATURE_ACTIVITYSAMPLING
#pragma warning restore 0649
#endif
            public string Name;                     // the name of the event
            public string Message;                  // If the event has a message associated with it, this is it.  
            public ParameterInfo[] Parameters;

            public TraceLoggingEventTypes TraceLoggingEventTypes;
            public EventActivityOptions ActivityOptions;
        };

        // This is the internal entry point that code:EventListeners call when wanting to send a command to a
        // eventSource. The logic is as follows
        // 
        // * if Command == Update
        //     * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies 
        //         to (if listener != null)
        //         perEventSourceSessionId = 0 - reserved for EventListeners
        //         perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
        //                  perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in 
        //                  Keywords that identifies the session
        //         perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are 
        //                  discriminated by etwSessionId
        //     * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
        //         activity tracing across different providers (which might have different sessionIds
        //         for the same ETW session)
        //     * enable, level, matchAnyKeywords are used to set a default for all events for the
        //         eventSource.  In particular, if 'enabled' is false, 'level' and
        //         'matchAnyKeywords' are not used.  
        //     * OnEventCommand is invoked, which may cause calls to
        //         code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
        //         depending on the logic in that routine.
        // * else (command != Update)
        //     * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
        //     * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).  
        // 
        // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
        internal void SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId,
                                  EventCommand command, bool enable,
                                  EventLevel level, EventKeywords matchAnyKeyword,
                                  IDictionary<string, string> commandArguments)
        {
            m_lastCommandException = null;
            bool shouldReport = (perEventSourceSessionId > 0) && (perEventSourceSessionId <= SessionMask.MAX);

            try
            {
                lock (EventListener.EventListenersLock)
                {
                    EnsureInitialized();
                    Contract.Assert(m_eventData != null);

                    // Find the per-EventSource dispatcher corresponding to registered dispatcher
                    EventDispatcher eventSourceDispatcher = GetDispatcher(listener);
                    if (eventSourceDispatcher == null && listener != null)     // dispatcher == null means ETW dispatcher
                        throw new ArgumentException(Environment.GetResourceString("EventSource_ListenerNotFound"));

                    if (commandArguments == null)
                        commandArguments = new Dictionary<string, string>();

                    if (command == EventCommand.Update)
                    {
                        // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
                        for (int i = 0; i < m_eventData.Length; i++)
                            EnableEventForDispatcher(eventSourceDispatcher, i, IsEnabledByDefault(i, enable, level, matchAnyKeyword));

                        if (enable)
                        {
                            if (!m_eventSourceEnabled)
                            {
                                // EventSource turned on for the first time, simply copy the bits.  
                                m_level = level;
                                m_matchAnyKeyword = matchAnyKeyword;
                            }
                            else
                            {
                                // Already enabled, make it the most verbose of the existing and new filter
                                if (level > m_level)
                                    m_level = level;
                                if (matchAnyKeyword == 0)
                                    m_matchAnyKeyword = 0;
                                else if (m_matchAnyKeyword != 0)
                                    m_matchAnyKeyword = unchecked(m_matchAnyKeyword | matchAnyKeyword);
                            }
                        }

                        // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to 
                        // represent 0-based positive values
                        bool bSessionEnable = (perEventSourceSessionId >= 0);
                        if (perEventSourceSessionId == 0 && enable == false)
                            bSessionEnable = false;

                        if (listener == null)
                        {
                            if (!bSessionEnable)
                                perEventSourceSessionId = -perEventSourceSessionId;
                            // for "global" enable/disable (passed in with listener == null and
                            //  perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
                            --perEventSourceSessionId;
                        }

                        command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;

                        // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
                        // hasn't changed.
                        // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
                        // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
                        Contract.Assert(perEventSourceSessionId >= -1 && perEventSourceSessionId <= SessionMask.MAX);

                        // Send the manifest if we are enabling an ETW session
                        if (bSessionEnable && eventSourceDispatcher == null)
                        {
                            // eventSourceDispatcher == null means this is the ETW manifest

                            // SendCommand can be called from the EventSource constructor as a side effect of 
                            // ETW registration.   Unfortunately when this callback is active the provider is
                            // not actually enabled (WriteEvents will fail).   Thus if we detect this condition
                            // (that we are still being constructed), we simply skip sending the manifest.  
                            // When the constructor completes we will try again and send the manifest at that time. 
                            //
                            // Note that we unconditionally send the manifest whenever we are enabled, even if
                            // we were already enabled.   This is because there may be multiple sessions active
                            // and we can't know that all the sessions have seen the manifest.  
                            if (m_completelyInited && !SelfDescribingEvents)
                                SendManifest(m_rawManifest);
                            else
                                m_deferedSendManifest = true;
                        }

#if FEATURE_ACTIVITYSAMPLING
                        if (bSessionEnable && perEventSourceSessionId != -1)
                        {
                            bool participateInSampling = false;
                            string activityFilters;
                            int sessionIdBit;

                            ParseCommandArgs(commandArguments, out participateInSampling,
                                            out activityFilters, out sessionIdBit);

                            if (listener == null && commandArguments.Count > 0 && perEventSourceSessionId != sessionIdBit)
                            {
                                throw new ArgumentException(Environment.GetResourceString("EventSource_SessionIdError",
                                                            perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD,
                                                            sessionIdBit + SessionMask.SHIFT_SESSION_TO_KEYWORD));
                            }

                            if (listener == null)
                            {
                                UpdateEtwSession(perEventSourceSessionId, etwSessionId, true, activityFilters, participateInSampling);
                            }
                            else
                            {
                                ActivityFilter.UpdateFilter(ref listener.m_activityFilter, this, 0, activityFilters);
                                eventSourceDispatcher.m_activityFilteringEnabled = participateInSampling;
                            }
                        }
                        else if (!bSessionEnable && listener == null)
                        {
                            // if we disable an ETW session, indicate that in a synthesized command argument
                            if (perEventSourceSessionId >= 0 && perEventSourceSessionId < SessionMask.MAX)
                            {
                                commandArguments["EtwSessionKeyword"] = (perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD).ToString(CultureInfo.InvariantCulture);
                            }
                        }
#endif // FEATURE_ACTIVITYSAMPLING

                        if (enable)
                        {
                            Contract.Assert(m_eventData != null);
                            m_eventSourceEnabled = true;
                        }

                        this.OnEventCommand(new EventCommandEventArgs(command, commandArguments, this, eventSourceDispatcher));

#if FEATURE_ACTIVITYSAMPLING
                        if (listener == null && !bSessionEnable && perEventSourceSessionId != -1)
                        {
                            // if we disable an ETW session, complete disabling it
                            UpdateEtwSession(perEventSourceSessionId, etwSessionId, false, null, false);
                        }
#endif // FEATURE_ACTIVITYSAMPLING

                        if (!enable)
                        {
                            // If we are disabling, maybe we can turn on 'quick checks' to filter
                            // quickly.  These are all just optimizations (since later checks will still filter)

#if FEATURE_ACTIVITYSAMPLING
                            // Turn off (and forget) any information about Activity Tracing.  
                            if (listener == null)
                            {
                                // reset all filtering information for activity-tracing-aware sessions
                                for (int i = 0; i < SessionMask.MAX; ++i)
                                {
                                    EtwSession etwSession = m_etwSessionIdMap[i];
                                    if (etwSession != null)
                                        ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
                                }
                                m_activityFilteringForETWEnabled = new SessionMask(0);
                                m_curLiveSessions = new SessionMask(0);
                                // reset activity-tracing-aware sessions
                                if (m_etwSessionIdMap != null)
                                    for (int i = 0; i < SessionMask.MAX; ++i)
                                        m_etwSessionIdMap[i] = null;
                                // reset legacy sessions
                                if (m_legacySessions != null)
                                    m_legacySessions.Clear();
                            }
                            else
                            {
                                ActivityFilter.DisableFilter(ref listener.m_activityFilter, this);
                                eventSourceDispatcher.m_activityFilteringEnabled = false;
                            }
#endif // FEATURE_ACTIVITYSAMPLING

                            // There is a good chance EnabledForAnyListener are not as accurate as
                            // they could be, go ahead and get a better estimate.  
                            for (int i = 0; i < m_eventData.Length; i++)
                            {
                                bool isEnabledForAnyListener = false;
                                for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
                                {
                                    if (dispatcher.m_EventEnabled[i])
                                    {
                                        isEnabledForAnyListener = true;
                                        break;
                                    }
                                }
                                m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
                            }

                            // If no events are enabled, disable the global enabled bit.
                            if (!AnyEventEnabled())
                            {
                                m_level = 0;
                                m_matchAnyKeyword = 0;
                                m_eventSourceEnabled = false;
                            }
                        }
#if FEATURE_ACTIVITYSAMPLING
                        UpdateKwdTriggers(enable);
#endif // FEATURE_ACTIVITYSAMPLING
                    }
                    else
                    {
                        if (command == EventCommand.SendManifest)
                        {
                            if (m_rawManifest != null)
                                SendManifest(m_rawManifest);
                        }

                        // These are not used for non-update commands and thus should always be 'default' values
                        // Contract.Assert(enable == true);
                        // Contract.Assert(level == EventLevel.LogAlways);
                        // Contract.Assert(matchAnyKeyword == EventKeywords.None);

                        this.OnEventCommand(new EventCommandEventArgs(command, commandArguments, null, null));
                    }

#if FEATURE_ACTIVITYSAMPLING
                    if (m_completelyInited && (listener != null || shouldReport))
                    {
                        SessionMask m = SessionMask.FromId(perEventSourceSessionId);
                        ReportActivitySamplingInfo(listener, m);
                    }
                    OutputDebugString(string.Format(CultureInfo.InvariantCulture, "{0}.SendCommand(session {1}, cmd {2}, enable {3}, level {4}): live sessions {5:x}, sampling {6:x}",
                                      m_name, perEventSourceSessionId, command, enable, level,
                                      (ulong)m_curLiveSessions, (ulong)m_activityFilteringForETWEnabled));
#endif // FEATURE_ACTIVITYSAMPLING
                }
            }
            catch (Exception e)
            {
                if (m_provider != null && m_provider.IsValid())
                {
                    // When the ETW session is created after the EventSource has registered with the ETW system
                    // we can send any error messages here.
                    ReportOutOfBandMessage("ERROR: Exception during SendCommand for EventSource " +
                                            Name + ": " + e.Message, true);
                }

                // When the EventSource is created while an ETW session already exists, the registration has not 
                // completed yet, and m_provider does not have a valid registration handle, so we'll remember 
                // the exception and rethrow. We'll call ReportOutOfBandMessage() in Initialize() at a point
                // where the ETW registration completed.
                m_lastCommandException = e;
                throw;
            }
        }

#if FEATURE_ACTIVITYSAMPLING

        internal void UpdateEtwSession(
            int sessionIdBit,
            int etwSessionId,
            bool bEnable,
            string activityFilters,
            bool participateInSampling)
        {
            if (sessionIdBit < SessionMask.MAX)
            {
                // activity-tracing-aware etw session
                if (bEnable)
                {
                    var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
                    ActivityFilter.UpdateFilter(ref etwSession.m_activityFilter, this, sessionIdBit, activityFilters);
                    m_etwSessionIdMap[sessionIdBit] = etwSession;
                    m_activityFilteringForETWEnabled[sessionIdBit] = participateInSampling;
                }
                else
                {
                    var etwSession = EtwSession.GetEtwSession(etwSessionId);
                    m_etwSessionIdMap[sessionIdBit] = null;
                    m_activityFilteringForETWEnabled[sessionIdBit] = false;
                    if (etwSession != null)
                    {
                        ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
                        // the ETW session is going away; remove it from the global list
                        EtwSession.RemoveEtwSession(etwSession);
                    }
                }
                m_curLiveSessions[sessionIdBit] = bEnable;
            }
            else
            {
                // legacy etw session    
                if (bEnable)
                {
                    if (m_legacySessions == null)
                        m_legacySessions = new List<EtwSession>(8);
                    var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
                    if (!m_legacySessions.Contains(etwSession))
                        m_legacySessions.Add(etwSession);
                }
                else
                {
                    var etwSession = EtwSession.GetEtwSession(etwSessionId);
                    if (etwSession != null)
                    {
                        if (m_legacySessions != null)
                            m_legacySessions.Remove(etwSession);
                        // the ETW session is going away; remove it from the global list
                        EtwSession.RemoveEtwSession(etwSession);
                    }
                }
            }
        }

        internal static bool ParseCommandArgs(
                        IDictionary<string, string> commandArguments,
                        out bool participateInSampling,
                        out string activityFilters,
                        out int sessionIdBit)
        {
            bool res = true;
            participateInSampling = false;
            string activityFilterString;
            if (commandArguments.TryGetValue("ActivitySamplingStartEvent", out activityFilters))
            {
                // if a start event is specified default the event source to participate in sampling
                participateInSampling = true;
            }

            if (commandArguments.TryGetValue("ActivitySampling", out activityFilterString))
            {
                if (string.Compare(activityFilterString, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
                    activityFilterString == "0")
                    participateInSampling = false;
                else
                    participateInSampling = true;
            }

            string sSessionKwd;
            int sessionKwd = -1;
            if (!commandArguments.TryGetValue("EtwSessionKeyword", out sSessionKwd) ||
                !int.TryParse(sSessionKwd, out sessionKwd) ||
                sessionKwd < SessionMask.SHIFT_SESSION_TO_KEYWORD ||
                sessionKwd >= SessionMask.SHIFT_SESSION_TO_KEYWORD + SessionMask.MAX)
            {
                sessionIdBit = -1;
                res = false;
            }
            else
            {
                sessionIdBit = sessionKwd - SessionMask.SHIFT_SESSION_TO_KEYWORD;
            }
            return res;
        }

        internal void UpdateKwdTriggers(bool enable)
        {
            if (enable)
            {
                // recompute m_keywordTriggers
                ulong gKeywords = unchecked((ulong)m_matchAnyKeyword);
                if (gKeywords == 0)
                    gKeywords = 0xFFFFffffFFFFffff;

                m_keywordTriggers = 0;
                for (int sessId = 0; sessId < SessionMask.MAX; ++sessId)
                {
                    EtwSession etwSession = m_etwSessionIdMap[sessId];
                    if (etwSession == null)
                        continue;

                    ActivityFilter activityFilter = etwSession.m_activityFilter;
                    ActivityFilter.UpdateKwdTriggers(activityFilter, m_guid, this, unchecked((EventKeywords)gKeywords));
                }
            }
            else
            {
                m_keywordTriggers = 0;
            }
        }

#endif // FEATURE_ACTIVITYSAMPLING

        /// <summary>
        /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
        /// of 'eventId.  If value is 'false' disable the event for that dispatcher.   If 'eventId' is out of
        /// range return false, otherwise true.  
        /// </summary>
        internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)
        {
            if (dispatcher == null)
            {
                if (eventId >= m_eventData.Length)
                    return false;
#if FEATURE_MANAGED_ETW
                if (m_provider != null)
                    m_eventData[eventId].EnabledForETW = value;
#endif
            }
            else
            {
                if (eventId >= dispatcher.m_EventEnabled.Length)
                    return false;
                dispatcher.m_EventEnabled[eventId] = value;
                if (value)
                    m_eventData[eventId].EnabledForAnyListener = true;
            }
            return true;
        }

        /// <summary>
        /// Returns true if any event at all is on.  
        /// </summary>
        private bool AnyEventEnabled()
        {
            for (int i = 0; i < m_eventData.Length; i++)
                if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener)
                    return true;
            return false;
        }

        private bool IsDisposed { get { return m_provider == null || m_provider.m_disposed; } }

        [SecuritySafeCritical]
        private void EnsureInitialized()
        {
#if !ES_BUILD_STANDALONE
            Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
#endif
            if (m_eventData == null)
            {
                Contract.Assert(m_rawManifest == null);
                m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
                Contract.Assert(m_eventData != null);

                foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
                {
                    EventSource eventSource = eventSourceRef.Target as EventSource;
                    if (eventSource != null && eventSource.Guid == m_guid && !eventSource.IsDisposed)
                    {
                        if (eventSource != this)
                            throw new ArgumentException(Environment.GetResourceString("EventSource_EventSourceGuidInUse", m_guid));
                    }
                }

                // Make certain all dispatchers also have their arrays initialized
                EventDispatcher dispatcher = m_Dispatchers;
                while (dispatcher != null)
                {
                    if (dispatcher.m_EventEnabled == null)
                        dispatcher.m_EventEnabled = new bool[m_eventData.Length];
                    dispatcher = dispatcher.m_Next;
                }
            }
            if (s_currentPid == 0)
            {
#if ES_BUILD_STANDALONE && !ES_BUILD_PCL
                // for non-BCL EventSource we must assert SecurityPermission
                new SecurityPermission(PermissionState.Unrestricted).Assert();
#endif
                s_currentPid = Win32Native.GetCurrentProcessId();
            }
        }

        // Send out the ETW manifest XML out to ETW
        // Today, we only send the manifest to ETW, custom listeners don't get it. 
        [SecuritySafeCritical]
        private unsafe bool SendManifest(byte[] rawManifest)
        {
            bool success = true;

            if (rawManifest == null)
                return false;

            Contract.Assert(!SelfDescribingEvents);

#if FEATURE_MANAGED_ETW
            fixed (byte* dataPtr = rawManifest)
            {
                // we don't want the manifest to show up in the event log channels so we specify as keywords 
                // everything but the first 8 bits (reserved for the 8 channels)
                var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
                ManifestEnvelope envelope = new ManifestEnvelope();

                envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
                envelope.MajorVersion = 1;
                envelope.MinorVersion = 0;
                envelope.Magic = 0x5B;              // An unusual number that can be checked for consistency. 
                int dataLeft = rawManifest.Length;
                envelope.ChunkNumber = 0;

                EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
                dataDescrs[0].Ptr = (ulong)&envelope;
                dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
                dataDescrs[0].Reserved = 0;

                dataDescrs[1].Ptr = (ulong)dataPtr;
                dataDescrs[1].Reserved = 0;

                int chunkSize = ManifestEnvelope.MaxChunkSize;
            TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
                envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
                while (dataLeft > 0)
                {
                    dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
                    if (m_provider != null)
                    {
                        if (!m_provider.WriteEvent(ref manifestDescr, null, null, 2, (IntPtr)dataDescrs))
                        {
                            // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
                            // can fail.   If we get this failure on the first chunk try again with something smaller
                            // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller. 
                            if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
                            {
                                if (envelope.ChunkNumber == 0 && chunkSize > 256)
                                {
                                    chunkSize = chunkSize / 2;
                                    goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
                                }
                            }
                            success = false;
                            if (ThrowOnEventWriteErrors)
                                ThrowEventSourceException();
                            break;
                        }
                    }
                    dataLeft -= chunkSize;
                    dataDescrs[1].Ptr += (uint)chunkSize;
                    envelope.ChunkNumber++;
                }
            }
#endif
            return success;
        }

#if ES_BUILD_PCL
        internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
        {
            return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
        }
#endif

        // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
        // When that is the case, we have the build the custom assemblies on a member by hand.         
        internal static Attribute GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
        {
            if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
            {
                // Let the runtime to the work for us, since we can execute code in this context.
                Attribute firstAttribute = null;
                foreach (var attribute in member.GetCustomAttributes(attributeType, false))
                {
                    firstAttribute = (Attribute)attribute;
                    break;
                }
                return firstAttribute;
            }

#if !ES_BUILD_PCL
            // In the reflection only context, we have to do things by hand.
            string fullTypeNameToFind = attributeType.FullName;

#if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
            fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
#endif

            foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
            {
                if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType))
                {
                    Attribute attr = null;

                    Contract.Assert(data.ConstructorArguments.Count <= 1);

                    if (data.ConstructorArguments.Count == 1)
                    {
                        attr = (Attribute)Activator.CreateInstance(attributeType, new object[] { data.ConstructorArguments[0].Value });
                    }
                    else if (data.ConstructorArguments.Count == 0)
                    {
                        attr = (Attribute)Activator.CreateInstance(attributeType);
                    }

                    if (attr != null)
                    {
                        Type t = attr.GetType();

                        foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
                        {
                            PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance);
                            object value = namedArgument.TypedValue.Value;

                            if (p.PropertyType.IsEnum)
                            {
                                value = Enum.Parse(p.PropertyType, value.ToString());
                            }

                            p.SetValue(attr, value, null);
                        }

                        return attr;
                    }
                }
            }

            return null;
#else // ES_BUILD_PCL
            throw new ArgumentException(Environment.GetResourceString("EventSource", "EventSource_PCLPlatformNotSupportedReflection"));
#endif
        }

        /// <summary>
        /// Evaluates if two related "EventSource"-domain types should be considered the same
        /// </summary>
        /// <param name="attributeType">The attribute type in the load context - it's associated with the running 
        /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
        /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
        /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to 
        /// </param>
        /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
        private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
        {
            return
                // are these the same type?
                attributeType == reflectedAttributeType ||
                // are the full typenames equal?
                string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
                // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
                // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
                    string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
                    attributeType.Namespace.EndsWith("Diagnostics.Tracing") &&
                    (reflectedAttributeType.Namespace.EndsWith("Diagnostics.Tracing")
#if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
                     || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing")
#endif
);
        }

        private static Type GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
        {
            // return false for "object" and interfaces
            if (eventSourceType.BaseType() == null)
                return null;

            // now go up the inheritance chain until hitting a concrete type ("object" at worse)
            do
            {
                eventSourceType = eventSourceType.BaseType();
            }
            while (eventSourceType != null && eventSourceType.IsAbstract());

            if (eventSourceType != null)
            {
                if (!allowEventSourceOverride)
                {
                    if (reflectionOnly && eventSourceType.FullName != typeof(EventSource).FullName ||
                        !reflectionOnly && eventSourceType != typeof(EventSource))
                        return null;
                }
                else
                {
                    if (eventSourceType.Name != "EventSource")
                        return null;
                }
            }
            return eventSourceType;
        }

        // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
        // return the UTF8 bytes.  It also sets up the code:EventData structures needed to dispatch events
        // at run time.  'source' is the event source to place the descriptors.  If it is null,
        // then the descriptors are not creaed, and just the manifest is generated.  
        private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source,
            EventManifestOptions flags = EventManifestOptions.None)
        {
            ManifestBuilder manifest = null;
            bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
            Exception exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
            byte[] res = null;

            if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
                return null;

#if DEBUG && ES_BUILD_STANDALONE
            TestSupport.TestHooks.MaybeThrow(eventSourceType,
                                        TestSupport.Category.ManifestError,
                                        "EventSource_CreateManifestAndDescriptors",
                                        new ArgumentException("EventSource_CreateManifestAndDescriptors"));
#endif

            try
            {
                MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                EventAttribute defaultEventAttribute;
                int eventId = 1;        // The number given to an event that does not have a explicitly given ID. 
                EventMetadata[] eventData = null;
                Dictionary<string, string> eventsByName = null;
                if (source != null || (flags & EventManifestOptions.Strict) != 0)
                {
                    eventData = new EventMetadata[methods.Length + 1];
                    eventData[0].Name = "";         // Event 0 is the 'write messages string' event, and has an empty name.
                }

                // See if we have localization information.  
                ResourceManager resources = null;
                EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
                if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
                    resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());

                manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
                                               resources, flags);

                // eventSourceType must be sealed and must derive from this EventSource
                if ((flags & EventManifestOptions.Strict) != 0)
                {
                    bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;

                    if (!typeMatch)
                        manifest.ManifestError(Environment.GetResourceString("EventSource_TypeMustDeriveFromEventSource"));
                    if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
                        manifest.ManifestError(Environment.GetResourceString("EventSource_TypeMustBeSealedOrAbstract"));
                }

                // Collect task, opcode, keyword and channel information
#if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
                foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
#else
                foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
#endif
                {
                    Type nestedType = eventSourceType.GetNestedType(providerEnumKind);
                    if (nestedType != null)
                    {
                        if (eventSourceType.IsAbstract())
                        {
                            manifest.ManifestError(Environment.GetResourceString("EventSource_AbstractMustNotDeclareKTOC", nestedType.Name));
                        }
                        else
                        {
                            foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
                            {
                                AddProviderEnumKind(manifest, staticField, providerEnumKind);
                            }
                        }
                    }
                }
                // ensure we have keywords for the session-filtering reserved bits
                {
                    manifest.AddKeyword("Session3", (long)0x1000 << 32);
                    manifest.AddKeyword("Session2", (long)0x2000 << 32);
                    manifest.AddKeyword("Session1", (long)0x4000 << 32);
                    manifest.AddKeyword("Session0", (long)0x8000 << 32);
                }

                if (eventSourceType.Name != "EventSource")
                {
                    for (int i = 0; i < methods.Length; i++)
                    {
                        MethodInfo method = methods[i];
                        ParameterInfo[] args = method.GetParameters();

                        // Get the EventDescriptor (from the Custom attributes)
                        EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);

                        // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for 
                        // the only reason of limiting the number of methods considered to be events. This broke a common 
                        // design of having event sources implement specific interfaces. To fix this in a compatible way
                        // we will now allow both non-void returning and virtual methods to be Event methods, as long 
                        // as they are marked with the [Event] attribute
                        if (/* method.IsVirtual || */ method.IsStatic)
                        {
                            continue;
                        }

                        if (eventSourceType.IsAbstract())
                        {
                            if (eventAttribute != null)
                                manifest.ManifestError(Environment.GetResourceString("EventSource_AbstractMustNotDeclareEventMethods", method.Name, eventAttribute.EventId));
                            continue;
                        }
                        else if (eventAttribute == null)
                        {
                            // Methods that don't return void can't be events, if they're NOT marked with [Event].
                            // (see Compat comment above)
                            if (method.ReturnType != typeof(void))
                            {
                                continue;
                            }

                            // Continue to ignore virtual methods if they do NOT have the [Event] attribute
                            // (see Compat comment above)
                            if (method.IsVirtual)
                            {
                                continue;
                            }

                            // If we explictly mark the method as not being an event, then honor that.  
                            if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
                                continue;

                            defaultEventAttribute = new EventAttribute(eventId);
                            eventAttribute = defaultEventAttribute;
                        }
                        else if (eventAttribute.EventId <= 0)
                        {
                            manifest.ManifestError(Environment.GetResourceString("EventSource_NeedPositiveId", method.Name), true);
                            continue;   // don't validate anything else for this event
                        }
                        if (method.Name.LastIndexOf('.') >= 0)
                            manifest.ManifestError(Environment.GetResourceString("EventSource_EventMustNotBeExplicitImplementation", method.Name, eventAttribute.EventId));

                        eventId++;
                        string eventName = method.Name;

                        if (!eventAttribute.IsOpcodeSet)
                        {
                            // By default pick a task ID derived from the EventID, starting with the highest task number and working back 
                            bool noTask = (eventAttribute.Task == EventTask.None);
                            if (eventAttribute.Task == EventTask.None)
                                eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);

                            // pick a default opcode (either Info or start or stop if the name ends with that suffix.  
                            eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);

                            // Make the stop opcode have the same task as the start opcode.
                            if (noTask) 
                            {
                                if (eventAttribute.Opcode == EventOpcode.Start)
                                {
                                    string taskName = eventName.Substring(0, eventName.Length-s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
                                    if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0  && 
                                        string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length-taskName.Length, s_ActivityStartSuffix.Length)) == 0)
                                    {
                                        // Add a task that is just the task name for the start event.   This supress the auto-task generation
                                        // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
                                        manifest.AddTask(taskName, (int) eventAttribute.Task);
                                    }
                                }
                                else if (eventAttribute.Opcode == EventOpcode.Stop)
                                {
                                    // Find the start associated with this stop event.  We requre start to be immediately before the stop
                                    int startEventId = eventAttribute.EventId-1;
                                    Contract.Assert(0 <= startEventId);                // Since we reserve id 0, we know that id-1 is <= 0
                                    EventMetadata startEventMetadata = eventData[startEventId];
                                    
                                    // If you remove the Stop and add a Start does that name match the Start Event's Name?
                                    // Ideally we would throw an error 
                                    string taskName = eventName.Substring(0, eventName.Length-s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
                                    if (startEventMetadata.Descriptor.Opcode == (byte) EventOpcode.Start &&
                                        string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0  && 
                                        string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length-taskName.Length, s_ActivityStartSuffix.Length)) == 0)
                                    {

                                        // Make the stop event match the start event
                                        eventAttribute.Task = (EventTask) startEventMetadata.Descriptor.Task;
                                    } 
                                    else if ((flags & EventManifestOptions.Strict) != 0)        // Throw an error if we can compatibly.   
                                        throw new ArgumentException(Environment.GetResourceString("EventSource_StopsFollowStarts"));
                                }
                            }
                        }
        
                        RemoveFirstArgIfRelatedActivityId(ref args);
                        if (!(source != null && source.SelfDescribingEvents))
                        {
                            manifest.StartEvent(eventName, eventAttribute);
                            for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
                            {
                                manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name);
                            }
                            manifest.EndEvent();
                        }

                        if (source != null || (flags & EventManifestOptions.Strict) != 0)
                        {
                            // Do checking for user errors (optional, but not a big deal so we do it).  
                            DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest);

#if FEATURE_MANAGED_ETW_CHANNELS
                            // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
                            // and is not required for the manifest
                            if (eventAttribute.Channel != EventChannel.None)
                            {
                                unchecked
                                {
                                    eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel);
                                }
                            }
#endif
                            string eventKey = "event_" + eventName;
                            string msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
                            // overwrite inline message with the localized message
                            if (msg != null) eventAttribute.Message = msg;

                            AddEventDescriptor(ref eventData, eventName, eventAttribute, args);
                        }
                    }
                }

                if (source != null)
                {
                    TrimEventDescriptors(ref eventData);
                    source.m_eventData = eventData;     // officialy initialize it. We do this at most once (it is racy otherwise). 
#if FEATURE_MANAGED_ETW_CHANNELS
                    source.m_channelData = manifest.GetChannelData();
#endif
                }

                // if this is an abstract event source we've already performed all the validation we can
                if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
                {
                    bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
#if FEATURE_MANAGED_ETW_CHANNELS
                                            || manifest.GetChannelData().Length > 0
#endif
;

                    // if the manifest is not needed and we're not requested to validate the event source return early
                    if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
                        return null;

                    res = manifest.CreateManifest();
                }
            }
            catch (Exception e)
            {
                // if this is a runtime manifest generation let the exception propagate
                if ((flags & EventManifestOptions.Strict) == 0)
                    throw;
                // else store it to include it in the Argument exception we raise below
                exception = e;
            }

            if ((flags & EventManifestOptions.Strict) != 0 && (manifest.Errors.Count > 0 || exception != null))
            {
                string msg = String.Empty;
                if (manifest.Errors.Count > 0)
                {
                    bool firstError = true;
                    foreach (string error in manifest.Errors)
                    {
                        if (!firstError)
                            msg += Environment.NewLine;
                        firstError = false;
                        msg += error;
                    }
                }
                else
                    msg = "Unexpected error: " + exception.Message;

                throw new ArgumentException(msg, exception);
            }

            return bNeedsManifest ? res : null;
        }

        private static void RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
        {
            // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.  
            if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
                string.Compare(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase) == 0)
            {
                var newargs = new ParameterInfo[args.Length - 1];
                Array.Copy(args, 1, newargs, 0, args.Length - 1);
                args = newargs;
            }
        }

        // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
        // to the manifest.  
        private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
        {
            bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
            Type staticFieldType = staticField.FieldType;
            if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
            {
                if (providerEnumKind != "Opcodes") goto Error;
                int value = (int)staticField.GetRawConstantValue();
                manifest.AddOpcode(staticField.Name, value);
            }
            else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
            {
                if (providerEnumKind != "Tasks") goto Error;
                int value = (int)staticField.GetRawConstantValue();
                manifest.AddTask(staticField.Name, value);
            }
            else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
            {
                if (providerEnumKind != "Keywords") goto Error;
                ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue());
                manifest.AddKeyword(staticField.Name, value);
            }
#if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
            else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
            {
                if (providerEnumKind != "Channels") goto Error;
                var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
                manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
            }
#endif
            return;
        Error:
            manifest.ManifestError(Environment.GetResourceString("EventSource_EnumKindMismatch", staticField.Name, staticField.FieldType.Name, providerEnumKind));
        }

        // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
        // with the code:EventAttribute 'eventAttribute'.  resourceManger may be null in which case we populate it
        // it is populated if we need to look up message resources
        private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName,
                                EventAttribute eventAttribute, ParameterInfo[] eventParameters)
        {
            if (eventData == null || eventData.Length <= eventAttribute.EventId)
            {
                EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
                Array.Copy(eventData, newValues, eventData.Length);
                eventData = newValues;
            }

            eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
                    eventAttribute.EventId,
                    eventAttribute.Version,
#if FEATURE_MANAGED_ETW_CHANNELS
                    (byte)eventAttribute.Channel,
#else
 (byte)0,
#endif
 (byte)eventAttribute.Level,
                    (byte)eventAttribute.Opcode,
                    (int)eventAttribute.Task,
                    unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));

            eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
            eventData[eventAttribute.EventId].Name = eventName;
            eventData[eventAttribute.EventId].Parameters = eventParameters;
            eventData[eventAttribute.EventId].Message = eventAttribute.Message;
            eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
        }

        // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
        // size after all event descriptors have been added. 
        private static void TrimEventDescriptors(ref EventMetadata[] eventData)
        {
            int idx = eventData.Length;
            while (0 < idx)
            {
                --idx;
                if (eventData[idx].Descriptor.EventId != 0)
                    break;
            }
            if (eventData.Length - idx > 2)      // allow one wasted slot. 
            {
                EventMetadata[] newValues = new EventMetadata[idx + 1];
                Array.Copy(eventData, newValues, newValues.Length);
                eventData = newValues;
            }
        }

        // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
        // when a listener gets attached to a eventSource
        internal void AddListener(EventListener listener)
        {
            lock (EventListener.EventListenersLock)
            {
                bool[] enabledArray = null;
                if (m_eventData != null)
                    enabledArray = new bool[m_eventData.Length];
                m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
                listener.OnEventSourceCreated(this);
            }
        }

        // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
        // index for two distinct events etc.  Throws exceptions when it finds something wrong. 
        private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName,
            EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
            ManifestBuilder manifest)
        {
            int evtId = eventAttribute.EventId;
            string evtName = method.Name;
            int eventArg = GetHelperCallFirstArg(method);
            if (eventArg >= 0 && evtId != eventArg)
            {
                manifest.ManifestError(Environment.GetResourceString("EventSource_MismatchIdToWriteEvent", evtName, evtId, eventArg), true);
            }

            if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
            {
                manifest.ManifestError(Environment.GetResourceString("EventSource_EventIdReused", evtName, evtId, eventData[evtId].Name), true);
            }

            for (int idx = 0; idx < eventData.Length; ++idx)
            {
                if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
                {
                    manifest.ManifestError(Environment.GetResourceString("EventSource_TaskOpcodePairReused",
                                            evtName, evtId, eventData[idx].Name, idx));
                }
            }

            // for non-default event opcodes the user must define a task!
            if (eventAttribute.Opcode != EventOpcode.Info &&
                (eventAttribute.Task == EventTask.None || eventAttribute.Task == (EventTask)(0xFFFE - evtId)))
            {
                manifest.ManifestError(Environment.GetResourceString("EventSource_EventMustHaveTaskIfNonDefaultOpcode", evtName, evtId));
            }

            // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
            //  (the reason we don't is backwards compat and the need for handling this as a non-fatal error
            //  by eventRegister.exe)
            // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
            // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
            // {
            //     throw new WarningException(Environment.GetResourceString("EventSource_EventNameDoesNotEqualTaskPlusOpcode"));
            // }

            if (eventsByName == null)
                eventsByName = new Dictionary<string, string>();

            if (eventsByName.ContainsKey(evtName))
                manifest.ManifestError(Environment.GetResourceString("EventSource_EventNameReused", evtName));

            eventsByName[evtName] = evtName;
        }

        /// <summary>
        /// This method looks at the IL and tries to pattern match against the standard
        /// 'boilerplate' event body 
        /// <code>
        ///     { if (Enabled()) WriteEvent(#, ...) } 
        /// </code>
        /// If the pattern matches, it returns the literal number passed as the first parameter to
        /// the WriteEvent.  This is used to find common user errors (mismatching this
        /// number with the EventAttribute ID).  It is only used for validation.   
        /// </summary>
        /// <param name="method">The method to probe.</param>
        /// <returns>The literal value or -1 if the value could not be determined. </returns>
        [SecuritySafeCritical]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
        static private int GetHelperCallFirstArg(MethodInfo method)
        {
#if !ES_BUILD_PCL
            // Currently searches for the following pattern
            // 
            // ...     // CAN ONLY BE THE INSTRUCTIONS BELOW
            // LDARG0
            // LDC.I4 XXX
            // ...     // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
            // CALL 
            // NOP     // 0 or more times
            // RET
            // 
            // If we find this pattern we return the XXX.  Otherwise we return -1.  
            (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
            byte[] instrs = method.GetMethodBody().GetILAsByteArray();
            int retVal = -1;
            for (int idx = 0; idx < instrs.Length; )
            {
                switch (instrs[idx])
                {
                    case 0: // NOP
                    case 1: // BREAK
                    case 2: // LDARG_0
                    case 3: // LDARG_1
                    case 4: // LDARG_2
                    case 5: // LDARG_3
                    case 6: // LDLOC_0
                    case 7: // LDLOC_1
                    case 8: // LDLOC_2
                    case 9: // LDLOC_3
                    case 10: // STLOC_0
                    case 11: // STLOC_1
                    case 12: // STLOC_2
                    case 13: // STLOC_3
                        break;
                    case 14: // LDARG_S
                    case 16: // STARG_S
                        idx++;
                        break;
                    case 20: // LDNULL
                        break;
                    case 21: // LDC_I4_M1
                    case 22: // LDC_I4_0
                    case 23: // LDC_I4_1
                    case 24: // LDC_I4_2
                    case 25: // LDC_I4_3
                    case 26: // LDC_I4_4
                    case 27: // LDC_I4_5
                    case 28: // LDC_I4_6
                    case 29: // LDC_I4_7
                    case 30: // LDC_I4_8
                        if (idx > 0 && instrs[idx - 1] == 2)  // preceeded by LDARG0
                            retVal = instrs[idx] - 22;
                        break;
                    case 31: // LDC_I4_S
                        if (idx > 0 && instrs[idx - 1] == 2)  // preceeded by LDARG0
                            retVal = instrs[idx + 1];
                        idx++;
                        break;
                    case 32: // LDC_I4
                        idx += 4;
                        break;
                    case 37: // DUP
                        break;
                    case 40: // CALL
                        idx += 4;

                        if (retVal >= 0)
                        {
                            // Is this call just before return?  
                            for (int search = idx + 1; search < instrs.Length; search++)
                            {
                                if (instrs[search] == 42)  // RET
                                    return retVal;
                                if (instrs[search] != 0)   // NOP
                                    break;
                            }
                        }
                        retVal = -1;
                        break;
                    case 44: // BRFALSE_S
                    case 45: // BRTRUE_S
                        retVal = -1;
                        idx++;
                        break;
                    case 57: // BRFALSE
                    case 58: // BRTRUE
                        retVal = -1;
                        idx += 4;
                        break;
                    case 103: // CONV_I1
                    case 104: // CONV_I2
                    case 105: // CONV_I4
                    case 106: // CONV_I8
                    case 109: // CONV_U4
                    case 110: // CONV_U8
                        break;
                    case 140: // BOX
                    case 141: // NEWARR
                        idx += 4;
                        break;
                    case 162: // STELEM_REF
                        break;
                    case 254: // PREFIX
                        idx++;
                        // Covers the CEQ instructions used in debug code for some reason.
                        if (idx >= instrs.Length || instrs[idx] >= 6)
                            goto default;
                        break;
                    default:
                        /* Contract.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
                            " at " + idx + " in method " + method.Name); */
                        return -1;
                }
                idx++;
            }
#endif
            return -1;
        }

        [Conditional("DEBUG")]
        internal static void OutputDebugString(string msg)
        {
#if !ES_BUILD_PCL
            msg = msg.TrimEnd('\r', '\n') +
                    string.Format(CultureInfo.InvariantCulture, ", Thrd({0})" + Environment.NewLine, Thread.CurrentThread.ManagedThreadId);
            System.Diagnostics.Debugger.Log(0, null, msg);
#endif
        }

        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
        internal void ReportOutOfBandMessage(string msg, bool flush)
        {
            // msg == null is a signal to flush what's accumulated in the buffer
            if (msg == null && flush)
            {
                if (!string.IsNullOrEmpty(m_deferredErrorInfo))
                {
                    WriteString(m_deferredErrorInfo);
                    m_deferredErrorInfo = String.Empty;
                }
                return;
            }

            if (!msg.EndsWith(Environment.NewLine, StringComparison.Ordinal))
                msg = msg + Environment.NewLine;

#if !ES_BUILD_PCL
            // send message to debugger without delay
            System.Diagnostics.Debugger.Log(0, null, msg);
#endif

            m_deferredErrorInfo = m_deferredErrorInfo + msg;
            if (flush)
            {
                // send message to the ETW listener if available
                WriteString(m_deferredErrorInfo);
                m_deferredErrorInfo = String.Empty;
            }
        }

        private EventSourceSettings ValidateSettings(EventSourceSettings settings)
        {
            var evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
                                EventSourceSettings.EtwSelfDescribingEventFormat;
            if ((settings & evtFormatMask) == evtFormatMask)
                throw new ArgumentException(Environment.GetResourceString("EventSource_InvalidEventFormat"), "settings");

            // If you did not explicitly ask for manifest, you get self-describing.  
            if ((settings & evtFormatMask) == 0)
                settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
            return settings;
        }

        private bool ThrowOnEventWriteErrors
        {
            get { return (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; }
            set
            {
                if (value) m_config |= EventSourceSettings.ThrowOnEventWriteErrors;
                else m_config &= ~EventSourceSettings.ThrowOnEventWriteErrors;
            }
        }

        private bool SelfDescribingEvents
        {
            get
            {
                Contract.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
                                ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
                return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
            }
            set
            {
                if (!value)
                {
                    m_config |= EventSourceSettings.EtwManifestEventFormat;
                    m_config &= ~EventSourceSettings.EtwSelfDescribingEventFormat;
                }
                else
                {
                    m_config |= EventSourceSettings.EtwSelfDescribingEventFormat;
                    m_config &= ~EventSourceSettings.EtwManifestEventFormat;
                }
            }
        }

#if FEATURE_ACTIVITYSAMPLING
        private void ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)
        {
            Contract.Assert(listener == null || (uint)sessions == (uint)SessionMask.FromId(0));

            for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
            {
                if (!sessions[perEventSourceSessionId])
                    continue;

                ActivityFilter af;
                if (listener == null)
                {
                    EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
                    Contract.Assert(etwSession != null);
                    af = etwSession.m_activityFilter;
                }
                else
                {
                    af = listener.m_activityFilter;
                }

                if (af == null)
                    continue;

                SessionMask m = new SessionMask();
                m[perEventSourceSessionId] = true;

                foreach (var t in af.GetFilterAsTuple(m_guid))
                {
                    WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: {1} = {2}", perEventSourceSessionId, t.Item1, t.Item2), m);
                }

                bool participateInSampling = (listener == null) ?
                                               m_activityFilteringForETWEnabled[perEventSourceSessionId] :
                                               GetDispatcher(listener).m_activityFilteringEnabled;
                WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: Activity Sampling support: {1}",
                                          perEventSourceSessionId, participateInSampling ? "enabled" : "disabled"), m);
            }
        }
#endif // FEATURE_ACTIVITYSAMPLING

        // private instance state 
        private string m_name;                          // My friendly name (privided in ctor)
        internal int m_id;                              // A small integer that is unique to this instance.  
        private Guid m_guid;                            // GUID representing the ETW eventSource to the OS.  
        internal volatile EventMetadata[] m_eventData;  // None per-event data
        private volatile byte[] m_rawManifest;          // Bytes to send out representing the event schema

        private EventSourceSettings m_config;      // configuration information

        // Enabling bits
        private bool m_eventSourceEnabled;              // am I enabled (any of my events are enabled for any dispatcher)
        internal EventLevel m_level;                    // higest level enabled by any output dispatcher
        internal EventKeywords m_matchAnyKeyword;       // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'

        // Dispatching state 
        internal volatile EventDispatcher m_Dispatchers;    // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
#if FEATURE_MANAGED_ETW
        private volatile OverideEventProvider m_provider;   // This hooks up ETW commands to our 'OnEventCommand' callback
#endif
        private bool m_completelyInited;                // The EventSource constructor has returned without exception.   
        private bool m_deferedSendManifest;             // We did not send the manifest in the startup path
        private Exception m_lastCommandException;       // If there was an exception during a command, this is it.  
        private Exception m_constructionException;      // If there was an exception construction, this is it 
        private string m_deferredErrorInfo;             // non-fatal error info accumulated during construction

        private string[] m_traits;                      // Used to implement GetTraits

        internal static uint s_currentPid;              // current process id, used in synthesizing quasi-GUIDs
        [ThreadStatic]
        private static int m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException

#if FEATURE_MANAGED_ETW_CHANNELS
        internal volatile ulong[] m_channelData;
#endif

#if FEATURE_ACTIVITYSAMPLING
        private SessionMask m_curLiveSessions;          // the activity-tracing aware sessions' bits
        private EtwSession[] m_etwSessionIdMap;         // the activity-tracing aware sessions
        private List<EtwSession> m_legacySessions;      // the legacy ETW sessions listening to this source
        internal long m_keywordTriggers;                // a bit is set if it corresponds to a keyword that's part of an enabled triggering event
        internal SessionMask m_activityFilteringForETWEnabled; // does THIS EventSource have activity filtering turned on for each ETW session
        static internal Action<Guid> s_activityDying;   // Fires when something calls SetCurrentThreadToActivity()
        // Also used to mark that activity tracing is on for some case
#endif // FEATURE_ACTIVITYSAMPLING

        // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
        // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
        ActivityTracker m_activityTracker;
        internal const string s_ActivityStartSuffix = "Start";
        internal const string s_ActivityStopSuffix = "Stop";

        // used for generating GUID from eventsource name
        private static readonly byte[] namespaceBytes = new byte[] {
            0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
            0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
        };

        #endregion
    }

    /// <summary>
    /// Enables specifying event source configuration options to be used in the EventSource constructor.
    /// </summary>
    [Flags]
    public enum EventSourceSettings
    {
        /// <summary>
        /// This specifies none of the special configuration options should be enabled.
        /// </summary>
        Default = 0,
        /// <summary>
        /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.  
        /// </summary>
        ThrowOnEventWriteErrors = 1,
        /// <summary>
        /// Setting this option is a directive to the ETW listener should use manifest-based format when
        /// firing events. This is the default option when defining a type derived from EventSource 
        /// (using the protected EventSource constructors).
        /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
        /// </summary>
        EtwManifestEventFormat = 4,
        /// <summary>
        /// Setting this option is a directive to the ETW listener should use self-describing event format
        /// when firing events. This is the default option when creating a new instance of the EventSource
        /// type (using the public EventSource constructors).  
        /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
        /// </summary>
        EtwSelfDescribingEventFormat = 8,
    }

    /// <summary>
    /// An EventListener represents a target for the events generated by EventSources (that is subclasses
    /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
    /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
    /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
    /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
    /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
    /// longer needed.
    /// <para>
    /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
    /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of 
    /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular 
    /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
    /// </para><para>
    /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
    /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
    /// </para><para>
    /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
    /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
    /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
    /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
    /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
    /// </para><para>
    /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
    /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
    /// that eventSource what events that dispatcher will receive.
    /// </para><para>
    /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
    /// override this method to do something useful with the data.
    /// </para><para>
    /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
    /// invariant associated with this callback is that every eventSource gets exactly one
    /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
    /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
    /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
    /// created.
    /// </para>
    /// </summary>
    public abstract class EventListener : IDisposable
    {
        /// <summary>
        /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
        /// them on).  
        /// </summary>
        protected EventListener()
        {
            lock (EventListenersLock)
            {
                // Disallow creating EventListener reentrancy. 
                if (s_CreatingListener)
                    throw new InvalidOperationException(Environment.GetResourceString("EventSource_ListenerCreatedInsideCallback"));

                try
                {
                    s_CreatingListener = true;

                    // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that 
                    // Those added sources see this listener.
                    this.m_Next = s_Listeners;
                    s_Listeners = this;

                    // Find all existing eventSources call OnEventSourceCreated to 'catchup'
                    // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback) 
                    // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
                    // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
                    // is created.
                    WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();

                    for (int i = 0; i < eventSourcesSnapshot.Length; i++)
                    {
                        WeakReference eventSourceRef = eventSourcesSnapshot[i];
                        EventSource eventSource = eventSourceRef.Target as EventSource;
                        if (eventSource != null)
                            eventSource.AddListener(this); // This will cause the OnEventSourceCreated callback to fire. 
                    }

                    Validate();
                }
                finally
                {
                    s_CreatingListener = false;
                }
            }
        }
        /// <summary>
        /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
        /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
        /// is the only way to actually make the listen die. Thus it is important that users of EventListener
        /// call Dispose when they are done with their logging.
        /// </summary>
#if ES_BUILD_STANDALONE
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
#endif
        public virtual void Dispose()
        {
            lock (EventListenersLock)
            {
                Contract.Assert(s_Listeners != null);
                if (s_Listeners != null)
                {
                    if (this == s_Listeners)
                    {
                        EventListener cur = s_Listeners;
                        s_Listeners = this.m_Next;
                        RemoveReferencesToListenerInEventSources(cur);
                    }
                    else
                    {
                        // Find 'this' from the s_Listeners linked list.  
                        EventListener prev = s_Listeners;
                        for (; ; )
                        {
                            EventListener cur = prev.m_Next;
                            if (cur == null)
                                break;
                            if (cur == this)
                            {
                                // Found our Listener, remove references to to it in the eventSources
                                prev.m_Next = cur.m_Next;       // Remove entry. 
                                RemoveReferencesToListenerInEventSources(cur);
                                break;
                            }
                            prev = cur;
                        }
                    }
                }
                Validate();
            }
        }
        // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
        // 'cleanup' associated with this object

        /// <summary>
        /// Enable all events from the eventSource identified by 'eventSource' to the current 
        /// dispatcher that have a verbosity level of 'level' or lower.
        ///   
        /// This call can have the effect of REDUCING the number of events sent to the 
        /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
        /// 
        /// This call never has an effect on other EventListeners.
        ///
        /// </summary>
        public void EnableEvents(EventSource eventSource, EventLevel level)
        {
            EnableEvents(eventSource, level, EventKeywords.None);
        }
        /// <summary>
        /// Enable all events from the eventSource identified by 'eventSource' to the current
        /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
        /// matching any of the bits in 'matchAnyKeyword'.
        /// 
        /// This call can have the effect of REDUCING the number of events sent to the 
        /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
        /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
        /// 
        /// This call never has an effect on other EventListeners.
        /// </summary>
        public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
        {
            EnableEvents(eventSource, level, matchAnyKeyword, null);
        }
        /// <summary>
        /// Enable all events from the eventSource identified by 'eventSource' to the current
        /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
        /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
        /// effect passing additional 'key-value' arguments 'arguments' might have.  
        /// 
        /// This call can have the effect of REDUCING the number of events sent to the 
        /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
        /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
        /// 
        /// This call never has an effect on other EventListeners.
        /// </summary>       
        public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)
        {
            if (eventSource == null)
            {
                throw new ArgumentNullException("eventSource");
            }
            Contract.EndContractBlock();

            eventSource.SendCommand(this, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
        }
        /// <summary>
        /// Disables all events coming from eventSource identified by 'eventSource'.  
        /// 
        /// This call never has an effect on other EventListeners.      
        /// </summary>
        public void DisableEvents(EventSource eventSource)
        {
            if (eventSource == null)
            {
                throw new ArgumentNullException("eventSource");
            }
            Contract.EndContractBlock();

            eventSource.SendCommand(this, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
        }

        /// <summary>
        /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
        /// This can happen for all existing EventSources when the EventListener is created
        /// as well as for any EventSources that come into existence after the EventListener
        /// has been created.
        /// 
        /// These 'catch up' events are called during the construction of the EventListener.
        /// Subclasses need to be prepared for that.
        /// 
        /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
        /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
        /// </summary>
        /// <param name="eventSource"></param>
        internal protected virtual void OnEventSourceCreated(EventSource eventSource) { }
        /// <summary>
        /// This method is called whenever an event has been written by a EventSource for which 
        /// the EventListener has enabled events.  
        /// </summary>
        internal protected abstract void OnEventWritten(EventWrittenEventArgs eventData);
        /// <summary>
        /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
        /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
        /// it useful to store additional information about each eventSource connected to it,
        /// and EventSourceIndex allows this extra information to be efficiently stored in a
        /// (growable) array (eg List(T)).
        /// </summary>
        static protected int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }

        #region private
        /// <summary>
        /// This routine adds newEventSource to the global list of eventSources, it also assigns the
        /// ID to the eventSource (which is simply the ordinal in the global list).
        /// 
        /// EventSources currently do not pro-actively remove themselves from this list. Instead
        /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
        /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
        /// that are in the list). This seems OK since the expectation is that EventSources
        /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
        /// global variables).
        /// </summary>
        /// <param name="newEventSource"></param>
        internal static void AddEventSource(EventSource newEventSource)
        {
            lock (EventListenersLock)
            {
                if (s_EventSources == null)
                    s_EventSources = new List<WeakReference>(2);

                // Periodically search the list for existing entries to reuse, this avoids
                // unbounded memory use if we keep recycling eventSources (an unlikely thing). 
                int newIndex = -1;
                if (s_EventSources.Count % 64 == 63)   // on every block of 64, fill up the block before continuing
                {
                    int i = s_EventSources.Count;      // Work from the top down. 
                    while (0 < i)
                    {
                        --i;
                        WeakReference weakRef = s_EventSources[i];
                        if (!weakRef.IsAlive)
                        {
                            newIndex = i;
                            weakRef.Target = newEventSource;
                            break;
                        }
                    }
                }
                if (newIndex < 0)
                {
                    newIndex = s_EventSources.Count;
                    s_EventSources.Add(new WeakReference(newEventSource));
                }
                newEventSource.m_id = newIndex;

                // Add every existing dispatcher to the new EventSource
                for (EventListener listener = s_Listeners; listener != null; listener = listener.m_Next)
                    newEventSource.AddListener(listener);

                Validate();
            }
        }

        /// <summary>
        /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
        /// eventSources in the appdomain.  
        /// 
        /// The EventListenersLock must be held before calling this routine. 
        /// </summary>
        private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
        {
            // Foreach existing EventSource in the appdomain
            foreach (WeakReference eventSourceRef in s_EventSources)
            {
                EventSource eventSource = eventSourceRef.Target as EventSource;
                if (eventSource != null)
                {
                    // Is the first output dispatcher the dispatcher we are removing?
                    if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
                        eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
                    else
                    {
                        // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.  
                        EventDispatcher prev = eventSource.m_Dispatchers;
                        for (; ; )
                        {
                            EventDispatcher cur = prev.m_Next;
                            if (cur == null)
                            {
                                Contract.Assert(false, "EventSource did not have a registered EventListener!");
                                break;
                            }
                            if (cur.m_Listener == listenerToRemove)
                            {
                                prev.m_Next = cur.m_Next;       // Remove entry. 
                                break;
                            }
                            prev = cur;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Checks internal consistency of EventSources/Listeners. 
        /// </summary>
        [Conditional("DEBUG")]
        internal static void Validate()
        {
            lock (EventListenersLock)
            {
                // Get all listeners 
                Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
                EventListener cur = s_Listeners;
                while (cur != null)
                {
                    allListeners.Add(cur, true);
                    cur = cur.m_Next;
                }

                // For all eventSources 
                int id = -1;
                foreach (WeakReference eventSourceRef in s_EventSources)
                {
                    id++;
                    EventSource eventSource = eventSourceRef.Target as EventSource;
                    if (eventSource == null)
                        continue;
                    Contract.Assert(eventSource.m_id == id, "Unexpected event source ID.");

                    // None listeners on eventSources exist in the dispatcher list.   
                    EventDispatcher dispatcher = eventSource.m_Dispatchers;
                    while (dispatcher != null)
                    {
                        Contract.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
                        dispatcher = dispatcher.m_Next;
                    }

                    // Every dispatcher is on Dispatcher List of every eventSource. 
                    foreach (EventListener listener in allListeners.Keys)
                    {
                        dispatcher = eventSource.m_Dispatchers;
                        for (; ; )
                        {
                            Contract.Assert(dispatcher != null, "Listener is not on all eventSources.");
                            if (dispatcher.m_Listener == listener)
                                break;
                            dispatcher = dispatcher.m_Next;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
        /// code:s_EventSources WeakReference list.  (We happen to use the s_EventSources list as
        /// the lock object)
        /// </summary>
        internal static object EventListenersLock
        {
            get
            {
                if (s_EventSources == null)
                    Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
                return s_EventSources;
            }
        }

        // Instance fields
        internal volatile EventListener m_Next;                         // These form a linked list in s_Listeners
#if FEATURE_ACTIVITYSAMPLING
        internal ActivityFilter m_activityFilter;                       // If we are filtering by activity on this Listener, this keeps track of it. 
#endif // FEATURE_ACTIVITYSAMPLING

        // static fields

        /// <summary>
        /// The list of all listeners in the appdomain.  Listeners must be explicitly disposed to remove themselves 
        /// from this list.   Note that EventSources point to their listener but NOT the reverse.  
        /// </summary>
        internal static EventListener s_Listeners;
        /// <summary>
        /// The list of all active eventSources in the appdomain.  Note that eventSources do NOT 
        /// remove themselves from this list this is a weak list and the GC that removes them may
        /// not have happened yet.  Thus it can contain event sources that are dead (thus you have 
        /// to filter those out.  
        /// </summary>
        internal static List<WeakReference> s_EventSources;

        /// <summary>
        /// Used to disallow reentrancy.  
        /// </summary>
        private static bool s_CreatingListener = false;
        #endregion
    }

    /// <summary>
    /// Passed to the code:EventSource.OnEventCommand callback
    /// </summary>
    public class EventCommandEventArgs : EventArgs
    {
        /// <summary>
        /// Gets the command for the callback.
        /// </summary>
        public EventCommand Command { get; private set; }

        /// <summary>
        /// Gets the arguments for the callback.
        /// </summary>
        public IDictionary<String, String> Arguments { get; private set; }

        /// <summary>
        /// Enables the event that has the specified identifier.
        /// </summary>
        /// <param name="eventId">Event ID of event to be enabled</param>
        /// <returns>true if eventId is in range</returns>
        public bool EnableEvent(int eventId)
        {
            if (Command != EventCommand.Enable && Command != EventCommand.Disable)
                throw new InvalidOperationException();
            return eventSource.EnableEventForDispatcher(dispatcher, eventId, true);
        }

        /// <summary>
        /// Disables the event that have the specified identifier.
        /// </summary>
        /// <param name="eventId">Event ID of event to be disabled</param>
        /// <returns>true if eventId is in range</returns>
        public bool DisableEvent(int eventId)
        {
            if (Command != EventCommand.Enable && Command != EventCommand.Disable)
                throw new InvalidOperationException();
            return eventSource.EnableEventForDispatcher(dispatcher, eventId, false);
        }

        #region private

        internal EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource, EventDispatcher dispatcher)
        {
            this.Command = command;
            this.Arguments = arguments;
            this.eventSource = eventSource;
            this.dispatcher = dispatcher;
        }

        internal EventSource eventSource;
        internal EventDispatcher dispatcher;

        #endregion
    }

    /// <summary>
    /// EventWrittenEventArgs is passed to the user-provided override for
    /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
    /// </summary>
    public class EventWrittenEventArgs : EventArgs
    {
        /// <summary>
        /// The name of the event.   
        /// </summary>
        public string EventName
        {
            get
            {
                if (m_eventName != null)
                {
                    return m_eventName;
                }
                else
                    return m_eventSource.m_eventData[EventId].Name;
            }
            internal set
            {
                m_eventName = value;
            }
        }

        /// <summary>
        /// Gets the event ID for the event that was written.
        /// </summary>
        public int EventId { get; internal set; }

        /// <summary>
        /// Gets the activity ID for the thread on which the event was written.
        /// </summary>
        public Guid ActivityId
        {
            [System.Security.SecurityCritical]
            get { return EventSource.CurrentThreadActivityId; }
        }

        /// <summary>
        /// Gets the related activity ID if one was specified when the event was written.
        /// </summary>
        public Guid RelatedActivityId
        {
            [System.Security.SecurityCritical]
            get;
            internal set;
        }

        /// <summary>
        /// Gets the payload for the event.
        /// </summary>
        public ReadOnlyCollection<Object> Payload { get; internal set; }

        /// <summary>
        /// Gets the payload argument names.
        /// </summary>
        public ReadOnlyCollection<string> PayloadNames 
        { 
            get
            {
                // For contract based events we create the list lazily.
                if (m_payloadNames == null)
                {
                    // Self described events are identified by id -1.
                    Contract.Assert(EventId != -1);

                    var names = new List<string>();
                    foreach(var parameter in m_eventSource.m_eventData[EventId].Parameters)
                    {
                        names.Add(parameter.Name);
                    }
                    m_payloadNames = new ReadOnlyCollection<string>(names);
                }

                return m_payloadNames;
            }

            internal set
            {
                m_payloadNames = value;
            }
        }

        /// <summary>
        /// Gets the event source object.
        /// </summary>
        public EventSource EventSource { get { return m_eventSource; } }

        /// <summary>
        /// Gets the keywords for the event.
        /// </summary>
        public EventKeywords Keywords { get { return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords; } }

        /// <summary>
        /// Gets the operation code for the event.
        /// </summary>
        public EventOpcode Opcode { get { return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode; } }

        /// <summary>
        /// Gets the task for the event.
        /// </summary>
        public EventTask Task { get { return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task; } }

        /// <summary>
        /// Any provider/user defined options associated with the event.  
        /// </summary>
        /// 
        public EventTags Tags { get { return m_eventSource.m_eventData[EventId].Tags; } }

        /// <summary>
        /// Gets the message for the event.
        /// </summary>
        public string Message
        {
            get
            {
                if (m_message != null)
                    return m_message;
                else
                    return m_eventSource.m_eventData[EventId].Message;
            }
            internal set
            {
                m_message = value;
            }
        }


#if FEATURE_MANAGED_ETW_CHANNELS
        /// <summary>
        /// Gets the channel for the event.
        /// </summary>
        public EventChannel Channel { get { return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel; } }
#endif

        /// <summary>
        /// Gets the version of the event.
        /// </summary>
        public byte Version { get { return m_eventSource.m_eventData[EventId].Descriptor.Version; } }

        /// <summary>
        /// Gets the level for the event.
        /// </summary>
        public EventLevel Level
        {
            get
            {
                if ((uint)EventId >= (uint)m_eventSource.m_eventData.Length)
                    return EventLevel.LogAlways;
                return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
            }
        }

        #region private
        internal EventWrittenEventArgs(EventSource eventSource)
        {
            m_eventSource = eventSource;
        }
        private string m_message;
        private string m_eventName;
        private EventSource m_eventSource;
        private ReadOnlyCollection<string> m_payloadNames;
        #endregion
    }

    /// <summary>
    /// Allows customizing defaults and specifying localization support for the event source class to which it is applied. 
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public sealed class EventSourceAttribute : Attribute
    {
        /// <summary>
        /// Overrides the ETW name of the event source (which defaults to the class name)
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged, 
        /// except when upgrading existing ETW providers to using event sources.
        /// </summary>
        public string Guid { get; set; }

        /// <summary>
        /// <para>
        /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps 
        /// can be localized to several languages if desired. This works by creating a ResX style string table 
        /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g. 
        /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the 
        /// resources. This name is the value of the LocalizationResources property. 
        /// </para><para>
        /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by 
        /// using the following resource naming scheme
        /// </para>
        ///     <para>* event_EVENTNAME</para>
        ///     <para>* task_TASKNAME</para>
        ///     <para>* keyword_KEYWORDNAME</para>
        ///     <para>* map_MAPNAME</para>
        /// <para>
        /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.   
        /// Note that the localized string for an event corresponds to the Message string, and can have {0} values 
        /// which represent the payload values.  
        /// </para>
        /// </summary>
        public string LocalizationResources { get; set; }
    }

    /// <summary>
    /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
    /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
    /// name of the method and its signature to generate basic schema information for the event. The
    /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
    /// desired.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public sealed class EventAttribute : Attribute
    {
        /// <summary>Construct an EventAttribute with specified eventId</summary>
        /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
        public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
        /// <summary>Event's ID</summary>
        public int EventId { get; private set; }
        /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
        public EventLevel Level { get; set; }
        /// <summary>Event's keywords: allows classification of events by "categories"</summary>
        public EventKeywords Keywords { get; set; }
        /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
        public EventOpcode Opcode
        {
            get
            {
                return m_opcode;
            }
            set
            {
                this.m_opcode = value;
                this.m_opcodeSet = true;
            }
        }

        internal bool IsOpcodeSet
        {
            get
            {
                return m_opcodeSet;
            }
        }

        /// <summary>Event's task: allows logical grouping of events</summary>
        public EventTask Task { get; set; }
#if FEATURE_MANAGED_ETW_CHANNELS
        /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
        public EventChannel Channel { get; set; }
#endif
        /// <summary>Event's version</summary>
        public byte Version { get; set; }

        /// <summary>
        /// This can be specified to enable formatting and localization of the event's payload. You can 
        /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced 
        /// with the 'ToString()' of the corresponding part of the  event payload.   
        /// </summary>
        public string Message { get; set; }

        /// <summary>
        /// User defined options associated with the event.  These do not have meaning to the EventSource but
        /// are passed through to listeners which given them semantics.  
        /// </summary>
        public EventTags Tags { get; set; }

        /// <summary>
        /// Allows fine control over the Activity IDs generated by start and stop events
        /// </summary>
        public EventActivityOptions ActivityOptions { get; set; }

        #region private
        EventOpcode m_opcode;
        private bool m_opcodeSet;
        #endregion
    }

    /// <summary>
    /// By default all instance methods in a class that subclasses code:EventSource that and return
    /// void are assumed to be methods that generate an event. This default can be overridden by specifying
    /// the code:NonEventAttribute
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public sealed class NonEventAttribute : Attribute
    {
        /// <summary>
        /// Constructs a default NonEventAttribute
        /// </summary>
        public NonEventAttribute() { }
    }

#if FEATURE_MANAGED_ETW_CHANNELS
    /// <summary>
    /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
    /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
    /// <code>
    ///     public static class Channels
    ///     {
    ///         [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
    ///         public const EventChannel Admin = (EventChannel)16;
    ///     
    ///         [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
    ///         public const EventChannel Operational = (EventChannel)17;
    ///     }
    /// </code>
    /// </summary>
    [AttributeUsage(AttributeTargets.Field)]
#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
    public 
#endif
    class EventChannelAttribute : Attribute
    {
        /// <summary>
        /// Specified whether the channel is enabled by default
        /// </summary>
        public bool Enabled { get; set; }

        /// <summary>
        /// Legal values are in EventChannelType
        /// </summary>
        public EventChannelType EventChannelType { get; set; }

#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
        /// <summary>
        /// Specifies the isolation for the channel
        /// </summary>
        public EventChannelIsolation Isolation { get; set; }

        /// <summary>
        /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
        /// See MSDN ((http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx) for details.
        /// </summary>
        public string Access { get; set; } 

        /// <summary>
        /// Allows importing channels defined in external manifests
        /// </summary>
        public string ImportChannel { get; set; }
#endif

    }

    /// <summary>
    /// Allowed channel types
    /// </summary>
#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
    public 
#endif
    enum EventChannelType
    {
        /// <summary>The admin channel</summary>
        Admin = 1,
        /// <summary>The operational channel</summary>
        Operational,
        /// <summary>The Analytic channel</summary>
        Analytic,
        /// <summary>The debug channel</summary>
        Debug,
    }

#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
    /// <summary>
    /// Allowed isolation levels. See MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx) 
    /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the 
    /// access permissions for the channel and backing file.
    /// </summary>
    public 
    enum EventChannelIsolation
    {
        /// <summary>
        /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
        /// </summary>
        Application = 1,
        /// <summary>
        /// All channels that specify System isolation use the same ETW session
        /// </summary>
        System,
        /// <summary>
        /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
        /// Using Custom isolation lets you control the access permissions for the channel and backing file.
        /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
        /// </summary>
        Custom,
    }
#endif
#endif

    /// <summary>
    /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
    /// </summary>
    public enum EventCommand
    {
        /// <summary>
        /// Update EventSource state
        /// </summary>
        Update = 0,
        /// <summary>
        /// Request EventSource to generate and send its manifest
        /// </summary>
        SendManifest = -1,
        /// <summary>
        /// Enable event
        /// </summary>
        Enable = -2,
        /// <summary>
        /// Disable event
        /// </summary>
        Disable = -3
    };


    #region private classes

#if FEATURE_ACTIVITYSAMPLING

    /// <summary>
    /// ActivityFilter is a helper structure that is used to keep track of run-time state
    /// associated with activity filtering. It is 1-1 with EventListeners (logically
    /// every listener has one of these, however we actually allocate them lazily), as well
    /// as 1-to-1 with tracing-aware EtwSessions.
    /// 
    /// This structure also keeps track of the sampling counts associated with 'trigger'
    /// events.  Because these trigger events are rare, and you typically only have one of
    /// them, we store them here as a linked list.
    /// </summary>
    internal sealed class ActivityFilter : IDisposable
    {
        /// <summary>
        /// Disable all activity filtering for the listener associated with 'filterList', 
        /// (in the session associated with it) that is triggered by any event in 'source'.
        /// </summary>
        public static void DisableFilter(ref ActivityFilter filterList, EventSource source)
        {
#if !ES_BUILD_STANDALONE
            Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
#endif

            if (filterList == null)
                return;

            ActivityFilter cur;
            // Remove it from anywhere in the list (except the first element, which has to 
            // be treated specially)
            ActivityFilter prev = filterList;
            cur = prev.m_next;
            while (cur != null)
            {
                if (cur.m_providerGuid == source.Guid)
                {
                    // update TriggersActivityTracking bit
                    if (cur.m_eventId >= 0 && cur.m_eventId < source.m_eventData.Length)
                        --source.m_eventData[cur.m_eventId].TriggersActivityTracking;

                    // Remove it from the linked list.
                    prev.m_next = cur.m_next;
                    // dispose of the removed node
                    cur.Dispose();
                    // update cursor
                    cur = prev.m_next;
                }
                else
                {
                    // update cursors
                    prev = cur;
                    cur = prev.m_next;
                }
            }

            // Sadly we have to treat the first element specially in linked list removal in C#
            if (filterList.m_providerGuid == source.Guid)
            {
                // update TriggersActivityTracking bit
                if (filterList.m_eventId >= 0 && filterList.m_eventId < source.m_eventData.Length)
                    --source.m_eventData[filterList.m_eventId].TriggersActivityTracking;

                // We are the first element in the list.   
                var first = filterList;
                filterList = first.m_next;
                // dispose of the removed node
                first.Dispose();
            }
            // the above might have removed the one ActivityFilter in the session that contains the 
            // cleanup delegate; re-create the delegate if needed
            if (filterList != null)
            {
                EnsureActivityCleanupDelegate(filterList);
            }
        }

        /// <summary>
        /// Currently this has "override" semantics. We first disable all filters
        /// associated with 'source', and next we add new filters for each entry in the 
        /// string 'startEvents'. participateInSampling specifies whether non-startEvents 
        /// always trigger or only trigger when current activity is 'active'.
        /// </summary>
        public static void UpdateFilter(
                                    ref ActivityFilter filterList,
                                    EventSource source,
                                    int perEventSourceSessionId,
                                    string startEvents)
        {
#if !ES_BUILD_STANDALONE
            Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
#endif

            // first remove all filters associated with 'source'
            DisableFilter(ref filterList, source);

            if (!string.IsNullOrEmpty(startEvents))
            {
                // ActivitySamplingStartEvents is a space-separated list of Event:Frequency pairs.
                // The Event may be specified by name or by ID. Errors in parsing such a pair 
                // result in the error being reported to the listeners, and the pair being ignored.
                // E.g. "CustomActivityStart:1000 12:10" specifies that for event CustomActivityStart
                // we should initiate activity tracing once every 1000 events, *and* for event ID 12
                // we should initiate activity tracing once every 10 events.
                string[] activityFilterStrings = startEvents.Split(' ');

                for (int i = 0; i < activityFilterStrings.Length; ++i)
                {
                    string activityFilterString = activityFilterStrings[i];
                    int sampleFreq = 1;
                    int eventId = -1;
                    int colonIdx = activityFilterString.IndexOf(':');
                    if (colonIdx < 0)
                    {
                        source.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
                            activityFilterString, false);
                        // ignore failure...
                        continue;
                    }
                    string sFreq = activityFilterString.Substring(colonIdx + 1);
                    if (!int.TryParse(sFreq, out sampleFreq))
                    {
                        source.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq, false);
                        continue;
                    }
                    activityFilterString = activityFilterString.Substring(0, colonIdx);
                    if (!int.TryParse(activityFilterString, out eventId))
                    {
                        // reset eventId
                        eventId = -1;
                        // see if it's an event name
                        for (int j = 0; j < source.m_eventData.Length; j++)
                        {
                            EventSource.EventMetadata[] ed = source.m_eventData;
                            if (ed[j].Name != null && ed[j].Name.Length == activityFilterString.Length &&
                                string.Compare(ed[j].Name, activityFilterString, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                eventId = ed[j].Descriptor.EventId;
                                break;
                            }
                        }
                    }
                    if (eventId < 0 || eventId >= source.m_eventData.Length)
                    {
                        source.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString, false);
                        continue;
                    }
                    EnableFilter(ref filterList, source, perEventSourceSessionId, eventId, sampleFreq);
                }
            }
        }

        /// <summary>
        /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
        /// </summary>
        public static ActivityFilter GetFilter(ActivityFilter filterList, EventSource source)
        {
            for (var af = filterList; af != null; af = af.m_next)
            {
                if (af.m_providerGuid == source.Guid && af.m_samplingFreq != -1)
                    return af;
            }
            return null;
        }

        /// <summary>
        /// Returns a session mask representing all sessions in which the activity 
        /// associated with the current thread is allowed  through the activity filter. 
        /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally 
        /// most of the time this is false as you can guarentee this event is NOT a 
        /// triggering event. If 'triggeringEvent' is true, then it checks the 
        /// 'EventSource' and 'eventID' of the event being logged to see if it is actually
        /// a trigger. If so it activates the current activity. 
        /// 
        /// If 'childActivityID' is present, it will be added to the active set if the 
        /// current activity is active.  
        /// </summary>
        [SecurityCritical]
        unsafe public static bool PassesActivityFilter(
                                    ActivityFilter filterList,
                                    Guid* childActivityID,
                                    bool triggeringEvent,
                                    EventSource source,
                                    int eventId)
        {
            Contract.Assert(filterList != null && filterList.m_activeActivities != null);
            bool shouldBeLogged = false;
            if (triggeringEvent)
            {
                for (ActivityFilter af = filterList; af != null; af = af.m_next)
                {
                    if (eventId == af.m_eventId && source.Guid == af.m_providerGuid)
                    {
                        // Update the sampling count with wrap-around
                        int curSampleCount, newSampleCount;
                        do
                        {
                            curSampleCount = af.m_curSampleCount;
                            if (curSampleCount <= 1)
                                newSampleCount = af.m_samplingFreq;        // Wrap around, counting down to 1
                            else
                                newSampleCount = curSampleCount - 1;
                        }
                        while (Interlocked.CompareExchange(ref af.m_curSampleCount, newSampleCount, curSampleCount) != curSampleCount);
                        // If we hit zero, then start tracking the activity.  
                        if (curSampleCount <= 1)
                        {
                            Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
                            Tuple<Guid, int> startId;
                            // only add current activity if it's not already a root activity
                            if (!af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId))
                            {
                                // EventSource.OutputDebugString(string.Format("  PassesAF - Triggering(session {0}, evt {1})", af.m_perEventSourceSessionId, eventId));
                                shouldBeLogged = true;
                                af.m_activeActivities[currentActivityId] = Environment.TickCount;
                                af.m_rootActiveActivities[currentActivityId] = Tuple.Create(source.Guid, eventId);
                            }
                        }
                        else
                        {
                            // a start event following a triggering start event
                            Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
                            Tuple<Guid, int> startId;
                            // only remove current activity if we added it
                            if (af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId) &&
                                startId.Item1 == source.Guid && startId.Item2 == eventId)
                            {
                                // EventSource.OutputDebugString(string.Format("Activity dying: {0} -> StartEvent({1})", currentActivityId, eventId));
                                // remove activity only from current logging scope (af)
                                int dummy;
                                af.m_activeActivities.TryRemove(currentActivityId, out dummy);
                            }
                        }
                        break;
                    }
                }
            }

            var activeActivities = GetActiveActivities(filterList);
            if (activeActivities != null)
            {
                // if we hadn't already determined this should be logged, test further
                if (!shouldBeLogged)
                {
                    shouldBeLogged = !activeActivities.IsEmpty &&
                                     activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId);
                }
                if (shouldBeLogged && childActivityID != null &&
                    ((EventOpcode)source.m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send))
                {
                    FlowActivityIfNeeded(filterList, null, childActivityID);
                    // EventSource.OutputDebugString(string.Format("  PassesAF - activity {0}", *childActivityID));
                }
            }
            // EventSource.OutputDebugString(string.Format("  PassesAF - shouldBeLogged(evt {0}) = {1:x}", eventId, shouldBeLogged));
            return shouldBeLogged;
        }

        [System.Security.SecuritySafeCritical]
        public static bool IsCurrentActivityActive(ActivityFilter filterList)
        {
            var activeActivities = GetActiveActivities(filterList);
            if (activeActivities != null &&
                activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId))
                return true;

            return false;
        }

        /// <summary>
        /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
        /// to list of active activities IF 'currentActivityId' is also active. Passing in a null
        /// value for  'currentActivityid' is an indication tha caller has already verified
        /// that the current activity is active.
        /// </summary>
        [SecurityCritical]
        unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)
        {
            Contract.Assert(childActivityID != null);

            var activeActivities = GetActiveActivities(filterList);
            Contract.Assert(activeActivities != null);

            // take currentActivityId == null to mean we *know* the current activity is "active"
            if (currentActivityId != null && !activeActivities.ContainsKey(*currentActivityId))
                return;

            if (activeActivities.Count > MaxActivityTrackCount)
            {
                TrimActiveActivityStore(activeActivities);
                // make sure current activity is still in the set:
                activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
            }
            // add child activity to list of actives
            activeActivities[*childActivityID] = Environment.TickCount;

        }

        /// <summary>
        /// </summary>
        public static void UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)
        {
            for (var af = activityFilter; af != null; af = af.m_next)
            {
                if ((sourceGuid == af.m_providerGuid) &&
                    (source.m_eventData[af.m_eventId].TriggersActivityTracking > 0 ||
                    ((EventOpcode)source.m_eventData[af.m_eventId].Descriptor.Opcode == EventOpcode.Send)))
                {
                    // we could be more precise here, if we tracked 'anykeywords' per session
                    unchecked
                    {
                        source.m_keywordTriggers |= (source.m_eventData[af.m_eventId].Descriptor.Keywords & (long)sessKeywords);
                    }
                }
            }
        }

        /// <summary>
        /// For the EventSource specified by 'sourceGuid' and the EventListener/EtwSession 
        /// associated with 'this' ActivityFilter list, return configured sequence of 
        /// [eventId, sampleFreq] pairs that defines the sampling policy.
        /// </summary>
        public IEnumerable<Tuple<int, int>> GetFilterAsTuple(Guid sourceGuid)
        {
            for (ActivityFilter af = this; af != null; af = af.m_next)
            {
                if (af.m_providerGuid == sourceGuid)
                    yield return Tuple.Create(af.m_eventId, af.m_samplingFreq);
            }
        }

        /// <summary>
        /// The cleanup being performed consists of removing the m_myActivityDelegate from
        /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
        /// </summary>
        public void Dispose()
        {
#if !ES_BUILD_STANDALONE
            Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
#endif
            // m_myActivityDelegate is still alive (held by the static EventSource.s_activityDying). 
            // Therefore we are ok to take a dependency on m_myActivityDelegate being valid even 
            // during the finalization of the ActivityFilter
            if (m_myActivityDelegate != null)
            {
                EventSource.s_activityDying = (Action<Guid>)Delegate.Remove(EventSource.s_activityDying, m_myActivityDelegate);
                m_myActivityDelegate = null;
            }
        }

        #region private

        /// <summary>
        /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever
        /// 'samplingFreq' times the event fires. You can have several of these forming a 
        /// linked list.
        /// </summary>
        private ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)
        {
            m_providerGuid = source.Guid;
            m_perEventSourceSessionId = perEventSourceSessionId;
            m_eventId = eventId;
            m_samplingFreq = samplingFreq;
            m_next = existingFilter;

            Contract.Assert(existingFilter == null ||
                            (existingFilter.m_activeActivities == null) == (existingFilter.m_rootActiveActivities == null));

            // if this is the first filter we add for this session, we need to create a new 
            // table of activities. m_activeActivities is common across EventSources in the same
            // session
            ConcurrentDictionary<Guid, int> activeActivities = null;
            if (existingFilter == null ||
                (activeActivities = GetActiveActivities(existingFilter)) == null)
            {
                m_activeActivities = new ConcurrentDictionary<Guid, int>();
                m_rootActiveActivities = new ConcurrentDictionary<Guid, Tuple<Guid, int>>();

                // Add a delegate to the 'SetCurrentThreadToActivity callback so that I remove 'dead' activities
                m_myActivityDelegate = GetActivityDyingDelegate(this);
                EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, m_myActivityDelegate);
            }
            else
            {
                m_activeActivities = activeActivities;
                m_rootActiveActivities = existingFilter.m_rootActiveActivities;
            }

        }

        /// <summary>
        /// Ensure there's at least one ActivityFilter in the 'filterList' that contains an
        /// activity-removing delegate for the listener/session associated with 'filterList'.
        /// </summary>
        private static void EnsureActivityCleanupDelegate(ActivityFilter filterList)
        {
            if (filterList == null)
                return;

            for (ActivityFilter af = filterList; af != null; af = af.m_next)
            {
                if (af.m_myActivityDelegate != null)
                    return;
            }

            // we didn't find a delegate
            filterList.m_myActivityDelegate = GetActivityDyingDelegate(filterList);
            EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, filterList.m_myActivityDelegate);
        }

        /// <summary>
        /// Builds the delegate to be called when an activity is dying. This is responsible
        /// for performing whatever cleanup is needed for the ActivityFilter list passed in.
        /// This gets "added" to EventSource.s_activityDying and ends up being called from
        /// EventSource.SetCurrentThreadActivityId and ActivityFilter.PassesActivityFilter.
        /// </summary>
        /// <returns>The delegate to be called when an activity is dying</returns>
        private static Action<Guid> GetActivityDyingDelegate(ActivityFilter filterList)
        {
            return (Guid oldActivity) =>
            {
                int dummy;
                filterList.m_activeActivities.TryRemove(oldActivity, out dummy);
                Tuple<Guid, int> dummyTuple;
                filterList.m_rootActiveActivities.TryRemove(oldActivity, out dummyTuple);
            };
        }

        /// <summary>
        /// Enables activity filtering for the listener associated with 'filterList', triggering on
        /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
        /// 
        /// if 'eventID' is out of range (e.g. negative), it means we are not triggering (but we are 
        /// activitySampling if something else triggered).  
        /// </summary>
        /// <returns>true if activity sampling is enabled the samplingFreq is non-zero </returns>
        private static bool EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)
        {
#if !ES_BUILD_STANDALONE
            Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
#endif
            Contract.Assert(samplingFreq > 0);
            Contract.Assert(eventId >= 0);

            filterList = new ActivityFilter(source, perEventSourceSessionId, eventId, samplingFreq, filterList);

            // Mark the 'quick Check' that indicates this is a trigger event.  
            // If eventId is out of range then this mark is not done which has the effect of ignoring 
            // the trigger.
            if (0 <= eventId && eventId < source.m_eventData.Length)
                ++source.m_eventData[eventId].TriggersActivityTracking;

            return true;
        }

        /// <summary>
        /// Normally this code never runs, it is here just to prevent run-away resource usage.  
        /// </summary>
        private static void TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)
        {
            if (activities.Count > MaxActivityTrackCount)
            {
                // Remove half of the oldest activity ids.  
                var keyValues = activities.ToArray();
                var tickNow = Environment.TickCount;

                // Sort by age, taking into account wrap-around.   As long as x and y are within 
                // 23 days of now then (0x7FFFFFFF & (tickNow - x.Value)) is the delta (even if 
                // TickCount wraps).  I then sort by DESCENDING age.  (that is oldest value first)
                Array.Sort(keyValues, (x, y) => (0x7FFFFFFF & (tickNow - y.Value)) - (0x7FFFFFFF & (tickNow - x.Value)));
                for (int i = 0; i < keyValues.Length / 2; i++)
                {
                    int dummy;
                    activities.TryRemove(keyValues[i].Key, out dummy);
                }
            }
        }

        private static ConcurrentDictionary<Guid, int> GetActiveActivities(
                                    ActivityFilter filterList)
        {
            for (ActivityFilter af = filterList; af != null; af = af.m_next)
            {
                if (af.m_activeActivities != null)
                    return af.m_activeActivities;
            }
            return null;
        }

        // m_activeActivities always points to the sample dictionary for EVERY ActivityFilter  
        // in the m_next list. The 'int' value in the m_activities set is a timestamp 
        // (Environment.TickCount) of when the entry was put in the system and is used to 
        // remove 'old' entries that if the set gets too big.
        ConcurrentDictionary<Guid, int> m_activeActivities;

        // m_rootActiveActivities holds the "root" active activities, i.e. the activities 
        // that were marked as active because a Start event fired on them. We need to keep
        // track of these to enable sampling in the scenario of an app's main thread that 
        // never explicitly sets distinct activity IDs as it executes. To handle these
        // situations we manufacture a Guid from the thread's ID, and:
        //   (a) we consider the firing of a start event when the sampling counter reaches 
        //       zero to mark the beginning of an interesting activity, and 
        //   (b) we consider the very next firing of the same start event to mark the
        //       ending of that activity.
        // We use a ConcurrentDictionary to avoid taking explicit locks.
        //   The key (a guid) represents the activity ID of the root active activity
        //   The value is made up of the Guid of the event provider and the eventId of
        //      the start event.
        ConcurrentDictionary<Guid, Tuple<Guid, int>> m_rootActiveActivities;
        Guid m_providerGuid;        // We use the GUID rather than object identity because we don't want to keep the eventSource alive
        int m_eventId;              // triggering event
        int m_samplingFreq;         // Counter reset to this when it hits 0
        int m_curSampleCount;       // We count down to 0 and then activate the activity. 
        int m_perEventSourceSessionId;  // session ID bit for ETW, 0 for EventListeners

        const int MaxActivityTrackCount = 100000;   // maximum number of tracked activities

        ActivityFilter m_next;      // We create a linked list of these
        Action<Guid> m_myActivityDelegate;
        #endregion
    };


    /// <summary>
    /// An EtwSession instance represents an activity-tracing-aware ETW session. Since these
    /// are limited to 8 concurrent sessions per machine (currently) we're going to store
    /// the active ones in a singly linked list.
    /// </summary>
    internal class EtwSession
    {
        public static EtwSession GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)
        {
            if (etwSessionId < 0)
                return null;

            EtwSession etwSession;
            foreach (var wrEtwSession in s_etwSessions)
            {
#if ES_BUILD_STANDALONE
                if ((etwSession = (EtwSession) wrEtwSession.Target) != null && etwSession.m_etwSessionId == etwSessionId)
                    return etwSession;
#else
                if (wrEtwSession.TryGetTarget(out etwSession) && etwSession.m_etwSessionId == etwSessionId)
                    return etwSession;
#endif
            }

            if (!bCreateIfNeeded)
                return null;

#if ES_BUILD_STANDALONE
            if (s_etwSessions == null)
                s_etwSessions = new List<WeakReference>();

            etwSession = new EtwSession(etwSessionId);
            s_etwSessions.Add(new WeakReference(etwSession));
#else
            if (s_etwSessions == null)
                s_etwSessions = new List<WeakReference<EtwSession>>();

            etwSession = new EtwSession(etwSessionId);
            s_etwSessions.Add(new WeakReference<EtwSession>(etwSession));
#endif

            if (s_etwSessions.Count > s_thrSessionCount)
                TrimGlobalList();

            return etwSession;

        }

        public static void RemoveEtwSession(EtwSession etwSession)
        {
            Contract.Assert(etwSession != null);
            if (s_etwSessions == null || etwSession == null)
                return;

            s_etwSessions.RemoveAll((wrEtwSession) =>
                {
                    EtwSession session;
#if ES_BUILD_STANDALONE
                    return (session = (EtwSession) wrEtwSession.Target) != null &&
                           (session.m_etwSessionId == etwSession.m_etwSessionId);
#else
                    return wrEtwSession.TryGetTarget(out session) &&
                           (session.m_etwSessionId == etwSession.m_etwSessionId);
#endif
                });

            if (s_etwSessions.Count > s_thrSessionCount)
                TrimGlobalList();
        }

        private static void TrimGlobalList()
        {
            if (s_etwSessions == null)
                return;

            s_etwSessions.RemoveAll((wrEtwSession) =>
                {
#if ES_BUILD_STANDALONE
                    return wrEtwSession.Target == null;
#else
                    EtwSession session;
                    return !wrEtwSession.TryGetTarget(out session);
#endif
                });
        }

        private EtwSession(int etwSessionId)
        {
            m_etwSessionId = etwSessionId;
        }

        public readonly int m_etwSessionId;        // ETW session ID (as retrieved by EventProvider)
        public ActivityFilter m_activityFilter;    // all filters enabled for this session

#if ES_BUILD_STANDALONE
        private static List<WeakReference> s_etwSessions = new List<WeakReference>();
#else
        private static List<WeakReference<EtwSession>> s_etwSessions = new List<WeakReference<EtwSession>>();
#endif
        private const int s_thrSessionCount = 16;
    }

#endif // FEATURE_ACTIVITYSAMPLING

    // holds a bitfield representing a session mask
    /// <summary>
    /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
    /// is the index in the SessionMask of the bit that will be set. These can translate to
    /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
    /// FromEventKeywords() methods.
    /// </summary>
    internal struct SessionMask
    {
        public SessionMask(SessionMask m)
        { m_mask = m.m_mask; }

        public SessionMask(uint mask = 0)
        { m_mask = mask & MASK; }

        public bool IsEqualOrSupersetOf(SessionMask m)
        {
            return (this.m_mask | m.m_mask) == this.m_mask;
        }

        public static SessionMask All
        {
            get { return new SessionMask(MASK); }
        }

        public static SessionMask FromId(int perEventSourceSessionId)
        {
            Contract.Assert(perEventSourceSessionId < MAX);
            return new SessionMask((uint)1 << perEventSourceSessionId);
        }

        public ulong ToEventKeywords()
        {
            return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
        }

        public static SessionMask FromEventKeywords(ulong m)
        {
            return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
        }

        public bool this[int perEventSourceSessionId]
        {
            get
            {
                Contract.Assert(perEventSourceSessionId < MAX);
                return (m_mask & (1 << perEventSourceSessionId)) != 0;
            }
            set
            {
                Contract.Assert(perEventSourceSessionId < MAX);
                if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
                else m_mask &= ~((uint)1 << perEventSourceSessionId);
            }
        }

        public static SessionMask operator |(SessionMask m1, SessionMask m2)
        {
            return new SessionMask(m1.m_mask | m2.m_mask);
        }

        public static SessionMask operator &(SessionMask m1, SessionMask m2)
        {
            return new SessionMask(m1.m_mask & m2.m_mask);
        }

        public static SessionMask operator ^(SessionMask m1, SessionMask m2)
        {
            return new SessionMask(m1.m_mask ^ m2.m_mask);
        }

        public static SessionMask operator ~(SessionMask m)
        {
            return new SessionMask(MASK & ~(m.m_mask));
        }

        public static explicit operator ulong(SessionMask m)
        { return m.m_mask; }

        public static explicit operator uint(SessionMask m)
        { return m.m_mask; }

        private uint m_mask;

        internal const int SHIFT_SESSION_TO_KEYWORD = 44;         // bits 44-47 inclusive are reserved
        internal const uint MASK = 0x0fU;      // the mask of 4 reserved bits
        internal const uint MAX = 4;          // maximum number of simultaneous ETW sessions supported
    }

    /// <summary>
    /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
    /// (m_EventEnabled) for a particular EventSource X EventListener tuple
    /// 
    /// Thus a single EventListener may have many EventDispatchers (one for every EventSource 
    /// that that EventListener has activate) and a Single EventSource may also have many
    /// event Dispatchers (one for every EventListener that has activated it). 
    /// 
    /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly  
    /// one EventListener (alhtough EventDispatcher does not 'remember' the EventSource it is
    /// associated with. 
    /// </summary>
    internal class EventDispatcher
    {
        internal EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)
        {
            m_Next = next;
            m_EventEnabled = eventEnabled;
            m_Listener = listener;
        }

        // Instance fields
        readonly internal EventListener m_Listener;   // The dispatcher this entry is for
        internal bool[] m_EventEnabled;               // For every event in a the eventSource, is it enabled?
#if FEATURE_ACTIVITYSAMPLING
        internal bool m_activityFilteringEnabled;     // does THIS EventSource have activity filtering turned on for this listener?
#endif // FEATURE_ACTIVITYSAMPLING

        // Only guarenteed to exist after a InsureInit()
        internal EventDispatcher m_Next;              // These form a linked list in code:EventSource.m_Dispatchers
        // Of all listeners for that eventSource.  
    }

    /// <summary>
    /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
    /// generated.
    /// </summary>
    [Flags]
    public enum EventManifestOptions
    {
        /// <summary>
        /// Only the resources associated with current UI culture are included in the  manifest
        /// </summary>
        None = 0x0,
        /// <summary>
        /// Throw exceptions for any inconsistency encountered
        /// </summary>
        Strict = 0x1,
        /// <summary>
        /// Generate a "resources" node under "localization" for every satellite assembly provided
        /// </summary>
        AllCultures = 0x2,
        /// <summary>
        /// Generate the manifest only if the event source needs to be registered on the machine,
        /// otherwise return null (but still perform validation if Strict is specified)
        /// </summary>
        OnlyIfNeededForRegistration = 0x4,
        /// <summary>
        /// When generating the manifest do *not* enforce the rule that the current EventSource class
        /// must be the base class for the user-defined type passed in. This allows validation of .net
        /// event sources using the new validation code
        /// </summary>
        AllowEventSourceOverride = 0x8,
    }

    /// <summary>
    /// ManifestBuilder is designed to isolate the details of the message of the event from the
    /// rest of EventSource.  This one happens to create XML. 
    /// </summary>
    internal class ManifestBuilder
    {
        /// <summary>
        /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
        /// 'resources, is a resource manager.  If specified all messages are localized using that manager.  
        /// </summary>
        public ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources,
                               EventManifestOptions flags)
        {
#if FEATURE_MANAGED_ETW_CHANNELS
            this.providerName = providerName;
#endif
            this.flags = flags;

            this.resources = resources;
            sb = new StringBuilder();
            events = new StringBuilder();
            templates = new StringBuilder();
            opcodeTab = new Dictionary<int, string>();
            stringTab = new Dictionary<string, string>();
            errors = new List<string>();
            perEventByteArrayArgIndices = new Dictionary<string, List<int>>();

            sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
            sb.AppendLine(" <instrumentation xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:win=\"http://manifests.microsoft.com/win/2004/08/windows/events\">");
            sb.AppendLine("  <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
            sb.Append("<provider name=\"").Append(providerName).
               Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
            if (dllName != null)
                sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);

            var symbolsName = providerName.Replace("-", "").Replace(".", "_");  // Period and - are illegal replace them.
            sb.Append("\" symbol=\"").Append(symbolsName);
            sb.Append("\">").AppendLine();
        }

        public void AddOpcode(string name, int value)
        {
            if ((flags & EventManifestOptions.Strict) != 0)
            {
                if (value <= 10 || value >= 239)
                    ManifestError(Environment.GetResourceString("EventSource_IllegalOpcodeValue", name, value));
                string prevName;
                if (opcodeTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
                    ManifestError(Environment.GetResourceString("EventSource_OpcodeCollision", name, prevName, value));
            }
            opcodeTab[value] = name;
        }
        public void AddTask(string name, int value)
        {
            if ((flags & EventManifestOptions.Strict) != 0)
            {
                if (value <= 0 || value >= 65535)
                    ManifestError(Environment.GetResourceString("EventSource_IllegalTaskValue", name, value));
                string prevName;
                if (taskTab != null && taskTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
                    ManifestError(Environment.GetResourceString("EventSource_TaskCollision", name, prevName, value));
            }
            if (taskTab == null)
                taskTab = new Dictionary<int, string>();
            taskTab[value] = name;
        }
        public void AddKeyword(string name, ulong value)
        {
            if ((value & (value - 1)) != 0)   // Is it a power of 2?
                ManifestError(Environment.GetResourceString("EventSource_KeywordNeedPowerOfTwo", "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
            if ((flags & EventManifestOptions.Strict) != 0)
            {
                if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
                    ManifestError(Environment.GetResourceString("EventSource_IllegalKeywordsValue", name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
                string prevName;
                if (keywordTab != null && keywordTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
                    ManifestError(Environment.GetResourceString("EventSource_KeywordCollision", name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
            }
            if (keywordTab == null)
                keywordTab = new Dictionary<ulong, string>();
            keywordTab[value] = name;
        }

#if FEATURE_MANAGED_ETW_CHANNELS
        /// <summary>
        /// Add a channel.  channelAttribute can be null
        /// </summary>
        public void AddChannel(string name, int value, EventChannelAttribute channelAttribute)
        {
            EventChannel chValue = (EventChannel)value;
            if (value < (int)EventChannel.Admin || value > 255)
                ManifestError(Environment.GetResourceString("EventSource_EventChannelOutOfRange", name, value));
            else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
                     channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
            {
                // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
                // but we want to allow them to override the default ones...
                ManifestError(Environment.GetResourceString("EventSource_ChannelTypeDoesNotMatchEventChannelValue",
                                                                            name, ((EventChannel)value).ToString()));
            }

            ulong kwd = GetChannelKeyword(chValue);

            if (channelTab == null)
                channelTab = new Dictionary<int, ChannelInfo>(4);
            channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
        }

        private EventChannelType EventChannelToChannelType(EventChannel channel)
        {
#if !ES_BUILD_STANDALONE
            Contract.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
#endif
            return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
        }
        private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
        {
            EventChannelAttribute attrib = new EventChannelAttribute();
            attrib.EventChannelType = EventChannelToChannelType(channel);
            if (attrib.EventChannelType <= EventChannelType.Operational)
                attrib.Enabled = true;
            return attrib;
        }

        public ulong[] GetChannelData()
        {
            if (this.channelTab == null)
            {
                return new ulong[0];
            }

            // We create an array indexed by the channel id for fast look up.
            // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
            int maxkey = -1;
            foreach (var item in this.channelTab.Keys)
            {
                if (item > maxkey)
                {
                    maxkey = item;
                }
            }

            ulong[] channelMask = new ulong[maxkey + 1];
            foreach (var item in this.channelTab)
            {
                channelMask[item.Key] = item.Value.Keywords;
            }

            return channelMask;
        }

#endif
        public void StartEvent(string eventName, EventAttribute eventAttribute)
        {
            Contract.Assert(numParams == 0);
            Contract.Assert(this.eventName == null);
            this.eventName = eventName;
            numParams = 0;
            byteArrArgIndices = null;

            events.Append("  <event").
                 Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
                 Append(" version=\"").Append(eventAttribute.Version).Append("\"").
                 Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
                 Append(" symbol=\"").Append(eventName).Append("\"");

            // at this point we add to the manifest's stringTab a message that is as-of-yet 
            // "untranslated to manifest convention", b/c we don't have the number or position 
            // of any byte[] args (which require string format index updates)
            WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);

            if (eventAttribute.Keywords != 0)
                events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
            if (eventAttribute.Opcode != 0)
                events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
            if (eventAttribute.Task != 0)
                events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
#if FEATURE_MANAGED_ETW_CHANNELS
            if (eventAttribute.Channel != 0)
            {
                events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
            }
#endif
        }

        public void AddEventParameter(Type type, string name)
        {
            if (numParams == 0)
                templates.Append("  <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
            if (type == typeof(byte[]))
            {
                // mark this index as "extraneous" (it has no parallel in the managed signature)
                // we use these values in TranslateToManifestConvention()
                if (byteArrArgIndices == null)
                    byteArrArgIndices = new List<int>(4);
                byteArrArgIndices.Add(numParams);

                // add an extra field to the template representing the length of the binary blob
                numParams++;
                templates.Append("   <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
            }
            numParams++;
            templates.Append("   <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");

            if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
            {
                // add "length" attribute to the "blob" field in the template (referencing the field added above)
                templates.Append(" length=\"").Append(name).Append("Size\"");
            }
            // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
            if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(UInt64) && Enum.GetUnderlyingType(type) != typeof(Int64))
            {
                templates.Append(" map=\"").Append(type.Name).Append("\"");
                if (mapsTab == null)
                    mapsTab = new Dictionary<string, Type>();
                if (!mapsTab.ContainsKey(type.Name))
                    mapsTab.Add(type.Name, type);        // Remember that we need to dump the type enumeration  
            }

            templates.Append("/>").AppendLine();
        }
        public void EndEvent()
        {
            if (numParams > 0)
            {
                templates.Append("  </template>").AppendLine();
                events.Append(" template=\"").Append(eventName).Append("Args\"");
            }
            events.Append("/>").AppendLine();

            if (byteArrArgIndices != null)
                perEventByteArrayArgIndices[eventName] = byteArrArgIndices;

            // at this point we have all the information we need to translate the C# Message
            // to the manifest string we'll put in the stringTab
            string msg;
            if (stringTab.TryGetValue("event_" + eventName, out msg))
            {
                msg = TranslateToManifestConvention(msg, eventName);
                stringTab["event_" + eventName] = msg;
            }

            eventName = null;
            numParams = 0;
            byteArrArgIndices = null;
        }

#if FEATURE_MANAGED_ETW_CHANNELS
        // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
        // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
        // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple 
        // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
        // to channels by the OS infrastructure).
        public ulong GetChannelKeyword(EventChannel channel)
        {
            if (channelTab == null)
            {
                channelTab = new Dictionary<int, ChannelInfo>(4);
            }

            if (channelTab.Count == MaxCountChannels)
                ManifestError(Environment.GetResourceString("EventSource_MaxChannelExceeded"));

            ulong channelKeyword;
            ChannelInfo info;
            if (!channelTab.TryGetValue((int)channel, out info))
            {
                channelKeyword = nextChannelKeywordBit;
                nextChannelKeywordBit >>= 1;
            }
            else
            {
                channelKeyword = info.Keywords;
            }

            return channelKeyword;
        }
#endif

        public byte[] CreateManifest()
        {
            string str = CreateManifestString();
            return Encoding.UTF8.GetBytes(str);
        }

        public IList<string> Errors { get { return errors; } }

        /// <summary>
        /// When validating an event source it adds the error to the error collection.
        /// When not validating it throws an exception if runtimeCritical is "true".
        /// Otherwise the error is ignored.
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="runtimeCritical"></param>
        public void ManifestError(string msg, bool runtimeCritical = false)
        {
            if ((flags & EventManifestOptions.Strict) != 0)
                errors.Add(msg);
            else if (runtimeCritical)
                throw new ArgumentException(msg);
        }

        private string CreateManifestString()
        {

#if FEATURE_MANAGED_ETW_CHANNELS
            // Write out the channels
            if (channelTab != null)
            {
                sb.Append(" <channels>").AppendLine();
                var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
                foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
                sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
                foreach (var kvpair in sortedChannels)
                {
                    int channel = kvpair.Key;
                    ChannelInfo channelInfo = kvpair.Value;

                    string channelType = null;
                    string elementName = "channel";
                    bool enabled = false;
                    string fullName = null;
#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
                    string isolation = null;
                    string access = null;
#endif
                    if (channelInfo.Attribs != null)
                    {
                        var attribs = channelInfo.Attribs;
                        if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
                            channelType = attribs.EventChannelType.ToString();
                        enabled = attribs.Enabled;
#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
                        if (attribs.ImportChannel != null)
                        {
                            fullName = attribs.ImportChannel;
                            elementName = "importChannel";
                        }
                        if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
                            isolation = attribs.Isolation.ToString();
                        access = attribs.Access;
#endif
                    }
                    if (fullName == null)
                        fullName = providerName + "/" + channelInfo.Name;

                    sb.Append("  <").Append(elementName);
                    sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
                    sb.Append(" name=\"").Append(fullName).Append("\"");
                    if (elementName == "channel")   // not applicable to importChannels.
                    {
                        WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
                        sb.Append(" value=\"").Append(channel).Append("\"");
                        if (channelType != null)
                            sb.Append(" type=\"").Append(channelType).Append("\"");
                        sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
                        if (access != null)
                            sb.Append(" access=\"").Append(access).Append("\"");
                        if (isolation != null)
                            sb.Append(" isolation=\"").Append(isolation).Append("\"");
#endif
                    }
                    sb.Append("/>").AppendLine();
                }
                sb.Append(" </channels>").AppendLine();
            }
#endif

            // Write out the tasks
            if (taskTab != null)
            {

                sb.Append(" <tasks>").AppendLine();
                var sortedTasks = new List<int>(taskTab.Keys);
                sortedTasks.Sort();
                foreach (int task in sortedTasks)
                {
                    sb.Append("  <task");
                    WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
                    sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
                }
                sb.Append(" </tasks>").AppendLine();
            }

            // Write out the maps
            if (mapsTab != null)
            {
                sb.Append(" <maps>").AppendLine();
                foreach (Type enumType in mapsTab.Values)
                {
                    string mapKind = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null ? "bitMap" : "valueMap";
                    sb.Append("  <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();

                    // write out each enum value 
                    FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
                    foreach (FieldInfo staticField in staticFields)
                    {
                        object constantValObj = staticField.GetRawConstantValue();
                        if (constantValObj != null)
                        {
                            string hexStr = null;
                            if (constantValObj is int)
                                hexStr = ((int)constantValObj).ToString("x", CultureInfo.InvariantCulture);
                            else if (constantValObj is long)
                                hexStr = ((long)constantValObj).ToString("x", CultureInfo.InvariantCulture);
                            sb.Append("   <map value=\"0x").Append(hexStr).Append("\"");
                            WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
                            sb.Append("/>").AppendLine();
                        }
                    }
                    sb.Append("  </").Append(mapKind).Append(">").AppendLine();
                }
                sb.Append(" </maps>").AppendLine();
            }

            // Write out the opcodes
            sb.Append(" <opcodes>").AppendLine();
            var sortedOpcodes = new List<int>(opcodeTab.Keys);
            sortedOpcodes.Sort();
            foreach (int opcode in sortedOpcodes)
            {
                sb.Append("  <opcode");
                WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
                sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
            }
            sb.Append(" </opcodes>").AppendLine();

            // Write out the keywords
            if (keywordTab != null)
            {
                sb.Append(" <keywords>").AppendLine();
                var sortedKeywords = new List<ulong>(keywordTab.Keys);
                sortedKeywords.Sort();
                foreach (ulong keyword in sortedKeywords)
                {
                    sb.Append("  <keyword");
                    WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
                    sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
                }
                sb.Append(" </keywords>").AppendLine();
            }

            sb.Append(" <events>").AppendLine();
            sb.Append(events);
            sb.Append(" </events>").AppendLine();

            sb.Append(" <templates>").AppendLine();
            if (templates.Length > 0)
            {
                sb.Append(templates);
            }
            else
            {
                // Work around a cornercase ETW issue where a manifest with no templates causes 
                // ETW events to not get sent to their associated channel.
                sb.Append("    <template tid=\"_empty\"></template>").AppendLine();
            }
            sb.Append(" </templates>").AppendLine();

            sb.Append("</provider>").AppendLine();
            sb.Append("</events>").AppendLine();
            sb.Append("</instrumentation>").AppendLine();

            // Output the localization information.  
            sb.Append("<localization>").AppendLine();

            List<CultureInfo> cultures = null;
            if (resources != null && (flags & EventManifestOptions.AllCultures) != 0)
            {
                cultures = GetSupportedCultures(resources);
            }
            else
            {
                cultures = new List<CultureInfo>();
                cultures.Add(CultureInfo.CurrentUICulture);
            }
#if ES_BUILD_STANDALONE
                var sortedStrings = new List<string>(stringTab.Keys);
                sortedStrings.Sort();
#else
            // DD 947936
            var sortedStrings = new string[stringTab.Keys.Count];
            stringTab.Keys.CopyTo(sortedStrings, 0);
            // Avoid using public Array.Sort as that attempts to access BinaryCompatibility. Unfortunately FrameworkEventSource gets called 
            // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory
            // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to
            // access BinaryCompatibility.
            ArraySortHelper<string>.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, Comparer<string>.Default);
#endif
            foreach (var ci in cultures)
            {
                sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
                sb.Append("  <stringTable>").AppendLine();

                foreach (var stringKey in sortedStrings)
                {
                    string val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
                    sb.Append("   <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
                }
                sb.Append("  </stringTable>").AppendLine();
                sb.Append(" </resources>").AppendLine();
            }
            sb.Append("</localization>").AppendLine();
            sb.AppendLine("</instrumentationManifest>");
            return sb.ToString();
        }

        #region private
        private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
        {
            stringBuilder.Append(" name=\"").Append(name).Append("\"");
            WriteMessageAttrib(sb, elementName, name, name);
        }
        private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)
        {
            string key = elementName + "_" + name;
            // See if the user wants things localized.  
            if (resources != null)
            {
                // resource fallback: strings in the neutral culture will take precedence over inline strings
                string localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
                if (localizedString != null)
                    value = localizedString;
            }
            if (value == null)
                return;

            stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
            string prevValue;
            if (stringTab.TryGetValue(key, out prevValue))
                ManifestError(Environment.GetResourceString("EventSource_DuplicateStringKey", key), true);
            else
                stringTab.Add(key, value);
        }
        internal string GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
        {
            string value = null;
            if (resources != null)
            {
                string localizedString = resources.GetString(key, ci);
                if (localizedString != null)
                {
                    value = localizedString;
                    if (etwFormat && key.StartsWith("event_"))
                    {
                        var evtName = key.Substring("event_".Length);
                        value = TranslateToManifestConvention(value, evtName);
                    }
                }
            }
            if (etwFormat && value == null)
                stringTab.TryGetValue(key, out value);

            return value;
        }

        /// <summary>
        /// There's no API to enumerate all languages an assembly is localized into, so instead
        /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite 
        /// assembly
        /// </summary>
        /// <param name="resources"></param>
        /// <returns></returns>
        private static List<CultureInfo> GetSupportedCultures(ResourceManager resources)
        {
            var cultures = new List<CultureInfo>();
#if !ES_BUILD_PCL && !FEATURE_CORECLR
            foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures /*| CultureTypes.NeutralCultures*/))
            {
                if (resources.GetResourceSet(ci, true, false) != null)
                    cultures.Add(ci);
            }
#endif // !ES_BUILD_PCL && !FEATURE_CORECLR
            if (!cultures.Contains(CultureInfo.CurrentUICulture))
                cultures.Insert(0, CultureInfo.CurrentUICulture);
            return cultures;
        }

        private static string GetLevelName(EventLevel level)
        {
            return (((int)level >= 16) ? "" : "win:") + level.ToString();
        }

#if FEATURE_MANAGED_ETW_CHANNELS
        private string GetChannelName(EventChannel channel, string eventName, string eventMessage)
        {
            ChannelInfo info = null;
            if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
            {
                if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
                    ManifestError(Environment.GetResourceString("EventSource_UndefinedChannel", channel, eventName));

                // allow channels to be auto-defined.  The well known ones get their well known names, and the
                // rest get names Channel<N>.  This allows users to modify the Manifest if they want more advanced features. 
                if (channelTab == null)
                    channelTab = new Dictionary<int, ChannelInfo>(4);
                
                string channelName = channel.ToString();        // For well know channels this is a nice name, otherwise a number 
                if (EventChannel.Debug < channel)
                    channelName = "Channel" + channelName;      // Add a 'Channel' prefix for numbers.  

                AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
                if (!channelTab.TryGetValue((int)channel, out info))
                    ManifestError(Environment.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
            }
            // events that specify admin channels *must* have non-null "Message" attributes
            if (resources != null && eventMessage == null)
                eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
            if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
                ManifestError(Environment.GetResourceString("EventSource_EventWithAdminChannelMustHaveMessage", eventName, info.Name));
            return info.Name;
        }
#endif
        private string GetTaskName(EventTask task, string eventName)
        {
            if (task == EventTask.None)
                return "";

            string ret;
            if (taskTab == null)
                taskTab = new Dictionary<int, string>();
            if (!taskTab.TryGetValue((int)task, out ret)) 
                ret = taskTab[(int)task] = eventName;
            return ret;
        }
        private string GetOpcodeName(EventOpcode opcode, string eventName)
        {
            switch (opcode)
            {
                case EventOpcode.Info:
                    return "win:Info";
                case EventOpcode.Start:
                    return "win:Start";
                case EventOpcode.Stop:
                    return "win:Stop";
                case EventOpcode.DataCollectionStart:
                    return "win:DC_Start";
                case EventOpcode.DataCollectionStop:
                    return "win:DC_Stop";
                case EventOpcode.Extension:
                    return "win:Extension";
                case EventOpcode.Reply:
                    return "win:Reply";
                case EventOpcode.Resume:
                    return "win:Resume";
                case EventOpcode.Suspend:
                    return "win:Suspend";
                case EventOpcode.Send:
                    return "win:Send";
                case EventOpcode.Receive:
                    return "win:Receive";
            }

            string ret;
            if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
            {
                ManifestError(Environment.GetResourceString("EventSource_UndefinedOpcode", opcode, eventName), true);
                ret = null;
            }
            return ret;
        }
        private string GetKeywords(ulong keywords, string eventName)
        {
            string ret = "";
            for (ulong bit = 1; bit != 0; bit <<= 1)
            {
                if ((keywords & bit) != 0)
                {
                    string keyword = null;
                    if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
                        (bit >= (ulong)0x1000000000000))
                    {
                        // do not report Windows reserved keywords in the manifest (this allows the code
                        // to be resilient to potential renaming of these keywords)
                        keyword = string.Empty;
                    }
                    if (keyword == null)
                    {
                        ManifestError(Environment.GetResourceString("EventSource_UndefinedKeyword", "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
                        keyword = string.Empty;
                    }
                    if (ret.Length != 0 && keyword.Length != 0)
                        ret = ret + " ";
                    ret = ret + keyword;
                }
            }
            return ret;
        }
        private string GetTypeName(Type type)
        {
            if (type.IsEnum())
            {
                FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                var typeName = GetTypeName(fields[0].FieldType);
                return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.  
            }
            switch (type.GetTypeCode())
            {
                case TypeCode.Boolean:
                    return "win:Boolean";
                case TypeCode.Byte:
                    return "win:UInt8";
                case TypeCode.Char:
                case TypeCode.UInt16:
                    return "win:UInt16";
                case TypeCode.UInt32:
                    return "win:UInt32";
                case TypeCode.UInt64:
                    return "win:UInt64";
                case TypeCode.SByte:
                    return "win:Int8";
                case TypeCode.Int16:
                    return "win:Int16";
                case TypeCode.Int32:
                    return "win:Int32";
                case TypeCode.Int64:
                    return "win:Int64";
                case TypeCode.String:
                    return "win:UnicodeString";
                case TypeCode.Single:
                    return "win:Float";
                case TypeCode.Double:
                    return "win:Double";
                case TypeCode.DateTime:
                    return "win:FILETIME";
                default:
                    if (type == typeof(Guid))
                        return "win:GUID";
                    else if (type == typeof(IntPtr))
                        return "win:Pointer";
                    else if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
                        return "win:Binary";
                    ManifestError(Environment.GetResourceString("EventSource_UnsupportedEventTypeInManifest", type.Name), true);
                    return string.Empty;
            }
        }

        private static void UpdateStringBuilder(ref StringBuilder stringBuilder, string eventMessage, int startIndex, int count)
        {
            if (stringBuilder == null)
                stringBuilder = new StringBuilder();
            stringBuilder.Append(eventMessage, startIndex, count);
        }

        // Manifest messages use %N conventions for their message substitutions.   Translate from
        // .NET conventions.   We can't use RegEx for this (we are in mscorlib), so we do it 'by hand' 
        private string TranslateToManifestConvention(string eventMessage, string evtName)
        {
            StringBuilder stringBuilder = null;        // We lazily create this 
            int writtenSoFar = 0;
            int chIdx = -1;
            for (int i = 0; ; )
            {
                if (i >= eventMessage.Length)
                {
                    if (stringBuilder == null)
                        return eventMessage;
                    UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
                    return stringBuilder.ToString();
                }

                if (eventMessage[i] == '%')
                {
                    // handle format message escaping character '%' by escaping it
                    UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
                    stringBuilder.Append("%%");
                    i++;
                    writtenSoFar = i;
                }
                else if (i < eventMessage.Length - 1 &&
                    (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
                {
                    // handle C# escaped '{" and '}'
                    UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
                    stringBuilder.Append(eventMessage[i]);
                    i++; i++;
                    writtenSoFar = i;
                }
                else if (eventMessage[i] == '{')
                {
                    int leftBracket = i;
                    i++;
                    int argNum = 0;
                    while (i < eventMessage.Length && Char.IsDigit(eventMessage[i]))
                    {
                        argNum = argNum * 10 + eventMessage[i] - '0';
                        i++;
                    }
                    if (i < eventMessage.Length && eventMessage[i] == '}')
                    {
                        i++;
                        UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
                        int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
                        stringBuilder.Append('%').Append(manIndex);
                        // An '!' after the insert specifier {n} will be interpreted as a literal.
                        // We'll escape it so that mc.exe does not attempt to consider it the 
                        // beginning of a format string.
                        if (i < eventMessage.Length && eventMessage[i] == '!')
                        {
                            i++;
                            stringBuilder.Append("%!");
                        }
                        writtenSoFar = i;
                    }
                    else
                    {
                        ManifestError(Environment.GetResourceString("EventSource_UnsupportedMessageProperty", evtName, eventMessage));
                    }
                }
                else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
                {
                    string[] escapes = { "&amp;", "&lt;", "&gt;", "&apos;", "&quot;", "%r", "%n", "%t" };
                    var update = new Action<char, string>(
                        (ch, escape) =>
                        {
                            UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
                            i++;
                            stringBuilder.Append(escape);
                            writtenSoFar = i;
                        });
                    update(eventMessage[i], escapes[chIdx]);
                }
                else
                    i++;
            }
        }

        private int TranslateIndexToManifestConvention(int idx, string evtName)
        {
            List<int> byteArrArgIndices;
            if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
            {
                foreach (var byArrIdx in byteArrArgIndices)
                {
                    if (idx >= byArrIdx)
                        ++idx;
                    else
                        break;
                }
            }
            return idx + 1;
        }

#if FEATURE_MANAGED_ETW_CHANNELS
        class ChannelInfo
        {
            public string Name;
            public ulong Keywords;
            public EventChannelAttribute Attribs;
        }
#endif

        Dictionary<int, string> opcodeTab;
        Dictionary<int, string> taskTab;
#if FEATURE_MANAGED_ETW_CHANNELS
        Dictionary<int, ChannelInfo> channelTab;
#endif
        Dictionary<ulong, string> keywordTab;
        Dictionary<string, Type> mapsTab;

        Dictionary<string, string> stringTab;       // Maps unlocalized strings to localized ones  

#if FEATURE_MANAGED_ETW_CHANNELS
        ulong nextChannelKeywordBit = 0x8000000000000000;   // available Keyword bit to be used for next channel definition
        const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
#endif

        StringBuilder sb;               // Holds the provider information. 
        StringBuilder events;           // Holds the events. 
        StringBuilder templates;

#if FEATURE_MANAGED_ETW_CHANNELS
        string providerName;
#endif
        ResourceManager resources;      // Look up localized strings here.  
        EventManifestOptions flags;
        IList<string> errors;           // list of currently encountered errors
        Dictionary<string, List<int>> perEventByteArrayArgIndices;  // "event_name" -> List_of_Indices_of_Byte[]_Arg

        // State we track between StartEvent and EndEvent.  
        string eventName;               // Name of the event currently being processed. 
        int numParams;                  // keeps track of the number of args the event has. 
        List<int> byteArrArgIndices;    // keeps track of the index of each byte[] argument
        #endregion
    }

    /// <summary>
    /// Used to send the m_rawManifest into the event dispatcher as a series of events.  
    /// </summary>
    internal struct ManifestEnvelope
    {
        public const int MaxChunkSize = 0xFF00;
        public enum ManifestFormats : byte
        {
            SimpleXmlFormat = 1,          // simply dump the XML manifest as UTF8
        }

        public ManifestFormats Format;
        public byte MajorVersion;
        public byte MinorVersion;
        public byte Magic;
        public ushort TotalChunks;
        public ushort ChunkNumber;
    };

    #endregion
}

